diff --git a/.clang-tidy b/.clang-tidy index 894d695..e721825 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,8 +1,7 @@ --- -Checks: '-*,performance-*,bugprone-*,readability-inconsistent-declaration-parameter-name,readability-string-compare,readability-braces-around-statements,modernize-deprecated-headers,modernize-make-unique,modernize-make-shared,modernize-use-override,modernize-use-equals-delete,modernize-use-emplace,modernize-use-bool-literals,modernize-redundant-void-arg,modernize-loop-convert,google-explicit-constructor,-bugprone-narrowing-conversions,-bugprone-macro-parentheses,-bugprone-suspicious-string-compare,-bugprone-incorrect-roundings,-bugprone-undefined-memory-manipulation,-bugprone-sizeof-expression,-bugprone-branch-clone,-bugprone-reserved-identifier,-bugprone-suspicious-include,-performance-no-int-to-ptr,-bugprone-easily-swappable-parameters,-bugprone-implicit-widening-of-multiplication-result' +Checks: '-*,bugprone-*,performance-*,readability-*,modernize-*,google-explicit-constructor,-bugprone-narrowing-conversions,-bugprone-incorrect-roundings,-bugprone-branch-clone,-bugprone-reserved-identifier,-bugprone-easily-swappable-parameters,-bugprone-implicit-widening-of-multiplication-result,-bugprone-assignment-in-if-condition,-bugprone-switch-missing-default-case,-performance-avoid-endl,-performance-enum-size,-readability-identifier-length,-readability-implicit-bool-conversion,-readability-magic-numbers,-readability-enum-initial-value,-readability-avoid-const-params-in-decls,-readability-redundant-inline-specifier,-readability-make-member-function-const,-readability-avoid-nested-conditional-operator,-readability-use-std-min-max,-readability-isolate-declaration,-readability-math-missing-parentheses,-readability-simplify-boolean-expr,-readability-else-after-return,-readability-function-cognitive-complexity,-readability-convert-member-functions-to-static,-readability-uppercase-literal-suffix,-readability-avoid-unconditional-preprocessor-if,-readability-redundant-casting,-readability-named-parameter,-readability-qualified-auto,-readability-container-data-pointer,-readability-suspicious-call-argument,-modernize-macro-to-enum,-modernize-use-trailing-return-type,-modernize-use-nodiscard,-modernize-use-using,-modernize-use-auto,-modernize-use-designated-initializers,-modernize-avoid-c-arrays,-modernize-use-default-member-init,-modernize-use-std-print,-modernize-return-braced-init-list,-modernize-pass-by-value,-modernize-avoid-bind,-modernize-concat-nested-namespaces,-clang-analyzer-*' WarningsAsErrors: '*' HeaderFilterRegex: '.' -AnalyzeTemporaryDtors: false FormatStyle: none User: user ... diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 825698c..c9af862 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,3 +4,5 @@ f7466e31408ba884c44728f7da04227863177241 # clang-format-14 wanted a bit of reformat a94a444e5acff86e04d1688d862e81a82108cf03 +# clang-format-15 wanted a bit of reformat also +b5aa09403e0f106f51292683c1841908167337ea diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b5db08a..93d00d4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ before_script: - echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list - apt-get update - apt-get build-dep --yes --no-install-recommends poppler - - apt-get install --yes --no-install-recommends ninja-build libcurl4-openssl-dev git ca-certificates locales libgtk-3-dev wget p7zip-full libbrotli-dev libboost-container-dev qt6-base-dev + - apt-get install --yes --no-install-recommends ninja-build libcurl4-openssl-dev git ca-certificates locales libgtk-3-dev libbrotli-dev libboost-container-dev qt6-base-dev - echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen - locale-gen @@ -22,7 +22,7 @@ variables: UPSTREAM_TEST_DATA_URL: https://gitlab.freedesktop.org/poppler/test.git cache: - key: "$CI_BUILD_NAME" + key: "$CI_JOB_NAME" paths: - build/ @@ -30,31 +30,35 @@ clang_format: stage: build before_script: - apt-get update - - apt-get install --yes --no-install-recommends git clang-format-14 + - apt-get install --yes --no-install-recommends git clang-format-19 script: - - find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format-14 -i {} \; + - find . \( -name "*.cpp" -or -name "*.h" -or -name "*.c" -or -name "*.cc" \) -exec clang-format-19 -i {} \; - git diff --exit-code build: stage: build script: + - apt-get update + - bash do-the-gnupg-2.4-dance.sh $PWD/build/gnupg/ - git clone --branch ${CI_COMMIT_REF_NAME} --depth 1 ${TEST_DATA_URL} test-data || git clone --depth 1 ${UPSTREAM_TEST_DATA_URL} test-data - mkdir -p build && cd build - - cmake -G Ninja -DTESTDATADIR=$PWD/../test-data .. - - ninja + - cmake -G Ninja -DTESTDATADIR=$PWD/../test-data -DCMAKE_PREFIX_PATH=$PWD/gnupg -DENABLE_UNSTABLE_API_ABI_HEADERS=ON -DVERIFY_PUBLIC_PRIVATE_HEADERS=true .. + - ninja -j ${FDO_CI_CONCURRENT} + - ninja -j ${FDO_CI_CONCURRENT} all_verify_interface_header_sets - ctest --output-on-failure -build_clang14_libcpp: +build_clang19_libcpp: stage: build script: - echo "We want to compile with C++23 here because it has some nice things like deleted std::string nullptr constructor" - - sed -i -e "s@CMAKE_CXX_STANDARD 17@CMAKE_CXX_STANDARD 23@g" CMakeLists.txt + - sed -i -e "s@CMAKE_CXX_STANDARD 20@CMAKE_CXX_STANDARD 23@g" CMakeLists.txt - git clone --branch ${CI_COMMIT_REF_NAME} --depth 1 ${TEST_DATA_URL} test-data || git clone --depth 1 ${UPSTREAM_TEST_DATA_URL} test-data - - apt-get install --yes --no-install-recommends libclang-14-dev llvm-14-dev libc++-14-dev libc++abi-14-dev clang-tidy-14 clang-14 libunwind-14-dev gperf jq + - apt-get install --yes --no-install-recommends libclang-19-dev llvm-19-dev libc++-19-dev libc++abi-19-dev clang-tidy-19 clang-19 libunwind-19-dev gperf jq - srcdir=`pwd` && mkdir -p /tmp/poppler_build && cd /tmp/poppler_build - - clang++-14 -fPIC -shared -o goostring-format-checker.so $srcdir/test/goostring-format-checker/goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -I /usr/lib/llvm-14/include/ - - CC=clang-14 CXX=clang++-14 cmake -G Ninja -DCMAKE_CXX_FLAGS="-stdlib=libc++ -Xclang -load -Xclang $PWD/goostring-format-checker.so -Xclang -add-plugin -Xclang goostring-format-checker -Werror -Wno-deprecated-declarations" -DTESTDATADIR=$srcdir/test-data -DCMAKE_EXPORT_COMPILE_COMMANDS=ON $srcdir - - ninja + - clang++-19 -fPIC -shared -o goostring-format-checker.so $srcdir/test/goostring-format-checker/goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -I /usr/lib/llvm-19/include/ + - echo "We disable Qt6 tests since Qt6 exposes std::string in its ABI which makes it not build in this CI since we're using libc++ but Qt6 in debian is build with libstdc++" + - CC=clang-19 CXX=clang++-19 cmake -DCMAKE_CXX_FLAGS="-stdlib=libc++ -Xclang -load -Xclang $PWD/goostring-format-checker.so -Xclang -add-plugin -Xclang goostring-format-checker -Werror -Wno-deprecated-declarations -Wno-error=unused-command-line-argument" -DTESTDATADIR=$srcdir/test-data -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_QT6_TESTS=OFF -DENABLE_GPGME=OFF $srcdir + - make -j ${FDO_CI_CONCURRENT} - ctest --output-on-failure - echo "This is a complex way of not running clang-tidy over autogenerated files, unfortunately -DCMAKE_CXX_CLANG_TIDY doesn't support that https://gitlab.kitware.com/cmake/cmake/-/issues/19772" - cat compile_commands.json | jq '[.[] | select(.file | contains("'"$srcdir"'"))]' > compile_commands.aux.json @@ -63,58 +67,90 @@ build_clang14_libcpp: - echo "Maybe we can replace this with NOLINTBEGIN in the future https://github.com/llvm/llvm-project/issues/56983" - find $srcdir/qt* -name *.cpp -exec sed -E -i '/#include .*moc"$/d' {} \; - cp "$srcdir/.clang-tidy" . - - run-clang-tidy-14 + - run-clang-tidy-19 -build_ubuntu_18_04: +build_ubuntu_22_04: stage: build - image: ubuntu:bionic + image: ubuntu:22.04 before_script: - apt-get update - - apt-get install --yes --no-install-recommends build-essential cmake ninja-build libjpeg-dev libopenjp2-7-dev qtbase5-dev gobject-introspection libglib2.0-dev libgtk-3-dev libgirepository1.0-dev libnss3-dev ca-certificates libcurl4-nss-dev liblcms2-dev libboost-container-dev libtiff-dev + - apt-get install --yes --no-install-recommends build-essential cmake ninja-build libjpeg-dev libopenjp2-7-dev qtbase5-dev gobject-introspection libglib2.0-dev libgtk-3-dev libgirepository1.0-dev libnss3-dev ca-certificates libcurl4-nss-dev liblcms2-dev libboost-container-dev libtiff-dev wget p7zip-full git qt6-base-dev clang-15 libc++-15-dev libc++abi-15-dev script: + - git clone --branch ${CI_COMMIT_REF_NAME} --depth 1 ${TEST_DATA_URL} test-data || git clone --depth 1 ${UPSTREAM_TEST_DATA_URL} test-data - mkdir -p build && cd build - - cmake -G Ninja .. + - cmake -G Ninja -DENABLE_GPGME=OFF -DTESTDATADIR=$PWD/../test-data .. - ninja + - ctest --output-on-failure + - cd .. && mkdir -p build_clang && cd build_clang + - CC=clang-15 CXX=clang++-15 cmake -DCMAKE_CXX_FLAGS="-stdlib=libc++ -fexperimental-library" -G Ninja -DENABLE_GPGME=OFF -DTESTDATADIR=$PWD/../test-data .. + - ninja + - ctest --output-on-failure -build_mingw64_fedora37: +build_mingw64_fedora41: stage: build - image: fedora:37 + image: fedora:41 before_script: - dnf install -y 'dnf-command(builddep)' - dnf builddep -y mingw64-poppler - - dnf -y install glibc-langpack-en make ninja-build mingw64-boost mingw64-curl mingw64-qt6-qtbase + - dnf -y install glibc-langpack-en make ninja-build mingw64-boost mingw64-curl mingw64-qt6-qtbase mingw64-gcc-c++ cmake-rpm-macros script: - mkdir -p build && cd build - - mingw64-cmake -G Ninja .. + - mingw64-cmake -DENABLE_NSS3=OFF -DENABLE_GPGME=OFF -G Ninja .. - ninja -build_clazy_clang14: +build_clazy_fedora41: stage: build + image: fedora:41 + before_script: + - dnf install -y 'dnf-command(builddep)' + - dnf builddep -y poppler + - dnf -y install clazy ninja-build glibc-langpack-en script: - - apt-get install --yes --no-install-recommends clazy clang-14 - mkdir -p build && cd build - - CC=clang-14 CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -G Ninja .. - - CLAZY_CHECKS="level0,level1,level2,isempty-vs-count,qhash-with-char-pointer-key,tr-non-literal,no-non-pod-global-static" ninja + - CC=clang-19 CXX=clazy CXXFLAGS="-Werror -Wno-deprecated-declarations" cmake -G Ninja .. + - CLAZY_CHECKS="level0,level1,level2,isempty-vs-count,qhash-with-char-pointer-key,tr-non-literal,no-non-pod-global-static" ninja -j ${FDO_CI_CONCURRENT} -build_android: +build_qt68_android: stage: build - image: kdeorg/android-sdk + image: invent-registry.kde.org/sysadmin/ci-images/android-qt68-ci before_script: - echo "workaround for ECM Android toolchain wanting all binaries to be shared libraries" - sed -i -e 's/ //g' /opt/nativetooling/share/ECM/toolchain/Android.cmake + - sudo apt install --yes wget script: + - wget https://download.savannah.gnu.org/releases/freetype/freetype-2.13.3.tar.xz + - tar xvfh freetype-2.13.3.tar.xz + - cd freetype-2.13.3 - mkdir -p build && cd build - 'ANDROID_ARCH_ABI=arm64-v8a cmake -G Ninja .. - -DCMAKE_ANDROID_API=28 - -DCMAKE_PREFIX_PATH="/opt/Qt/;/opt/kdeandroid-arm64/" + -DCMAKE_ANDROID_API=29 + -DCMAKE_BUILD_TYPE=debug + -DCMAKE_POSITION_INDEPENDENT_CODE=OFF + -DCMAKE_TOOLCHAIN_FILE=/home/user/android-arm-clang/lib/cmake/Qt6/qt.toolchain.cmake + -DQT_CHAINLOAD_TOOLCHAIN_FILE=/opt/nativetooling/share/ECM/toolchain/Android.cmake + -DCMAKE_INSTALL_PREFIX=/home/user/android-arm-clang + -DBUILD_SHARED_LIBS=ON' + - ninja -j ${FDO_CI_CONCURRENT} install + - cd ../.. + - mkdir -p build && cd build + - 'ANDROID_ARCH_ABI=arm64-v8a cmake -G Ninja .. + -DCMAKE_ANDROID_API=29 + -DCMAKE_PREFIX_PATH="/home/user/android-arm-clang" -DCMAKE_BUILD_TYPE=debug -DCMAKE_POSITION_INDEPENDENT_CODE=OFF -DENABLE_DCTDECODER=unmaintained -DENABLE_LIBOPENJPEG=unmaintained -DENABLE_BOOST=OFF + -DENABLE_LCMS=OFF + -DENABLE_LIBCURL=OFF + -DENABLE_LIBTIFF=OFF + -DENABLE_QT5=OFF + -DENABLE_NSS3=OFF + -DENABLE_GPGME=OFF -DCMAKE_CXX_FLAGS="-Werror -Wno-deprecated-declarations" - -DCMAKE_TOOLCHAIN_FILE=/opt/nativetooling/share/ECM/toolchain/Android.cmake' - - ninja + -DCMAKE_TOOLCHAIN_FILE=/home/user/android-arm-clang/lib/cmake/Qt6/qt.toolchain.cmake + -DQT_CHAINLOAD_TOOLCHAIN_FILE=/opt/nativetooling/share/ECM/toolchain/Android.cmake' + - ninja -j ${FDO_CI_CONCURRENT} qt5_docs: only: @@ -164,8 +200,8 @@ glib_docs: script: - apt-get install --yes --no-install-recommends gtk-doc-tools - mkdir -p build && cd build - - cmake -G Ninja -DENABLE_GTK_DOC=YES .. - - ninja glib-docs + - cmake -G Ninja -DENABLE_GTK_DOC=YES -DENABLE_GPGME=OFF .. + - ninja -j ${FDO_CI_CONCURRENT} glib-docs cache: {} artifacts: paths: diff --git a/CMakeLists.txt b/CMakeLists.txt index c53a626..b76d9a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0 FATAL_ERROR) +cmake_minimum_required(VERSION 3.22.0 FATAL_ERROR) project(poppler) @@ -26,6 +26,7 @@ CHECK_FILE_OFFSET_BITS() include(GenerateExportHeader) include(GNUInstallDirs) +include(CMakePushCheckState) set(ENABLE_FUZZER FALSE) @@ -38,23 +39,24 @@ if (ECM_FOUND) endif() endif() -set(POPPLER_MAJOR_VERSION "22") -set(POPPLER_MINOR_VERSION_STRING "12") +set(POPPLER_MAJOR_VERSION "25") +set(POPPLER_MINOR_VERSION_STRING "03") # We want the string version to have 08 but the integer version can't have a leading 0 since otherwise it's considered octal # So strip a leading 0 if found in POPPLER_MINOR_VERSION_STRING and store the result in POPPLER_MINOR_VERSION string(REGEX REPLACE "^0?(.+)$" "\\1" POPPLER_MINOR_VERSION "${POPPLER_MINOR_VERSION_STRING}") set(POPPLER_MICRO_VERSION "0") set(POPPLER_VERSION "${POPPLER_MAJOR_VERSION}.${POPPLER_MINOR_VERSION_STRING}.${POPPLER_MICRO_VERSION}") -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD 17) set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE) set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +set(CMAKE_VERIFY_INTERFACE_HEADER_SETS ON) # command line switches @@ -73,14 +75,18 @@ option(ENABLE_GTK_DOC "Whether to generate glib API documentation." OFF) option(ENABLE_QT5 "Compile poppler qt5 wrapper." ON) option(ENABLE_QT6 "Compile poppler qt6 wrapper." ON) set(ENABLE_LIBOPENJPEG "openjpeg2" CACHE STRING "Use libopenjpeg for JPX streams. Possible values: openjpeg2, unmaintained, none. 'unmaintained' gives you the internal unmaintained decoder. Use at your own risk. 'none' compiles no JPX decoder at all. Default: openjpeg2") -set(ENABLE_CMS "lcms2" CACHE STRING "Use color management system. Possible values: lcms2, none. 'none' disables color management system.") set(ENABLE_DCTDECODER "libjpeg" CACHE STRING "Use libjpeg for DCT streams. Possible values: libjpeg, unmaintained, none. will use libjpeg if available or fail if not. 'unmaintained' gives you the internal unmaintained decoder. Use at your own risk. 'none' compiles no DCT decoder at all. Default: libjpeg") +option(ENABLE_LCMS "Use LCMS for color management." ON) option(ENABLE_LIBCURL "Build libcurl based HTTP support." ON) -option(ENABLE_ZLIB "Build with zlib." ON) +option(ENABLE_LIBTIFF "Build code to write images as TIFF (pdfimages/pdftocairo/etc)." ON) +option(ENABLE_NSS3 "Build the NSS backend for cryptographic support" ON) +option(ENABLE_GPGME "Build the GPG backend for cryptographic support" ON) +option(ENABLE_PGP_SIGNATURES "Enable pgp signatures in GPG backend by default" OFF) option(ENABLE_ZLIB_UNCOMPRESS "Use zlib to uncompress flate streams (not totally safe)." OFF) option(USE_FLOAT "Use single precision arithmetic in the Splash backend" OFF) option(BUILD_SHARED_LIBS "Build poppler as a shared library" ON) option(RUN_GPERF_IF_PRESENT "Run gperf if it is found" ON) +option(INSTALL_GLIB_DEMO "Whether to install the poppler-glib-demo program." OFF) if(WIN32) option(ENABLE_RELOCATABLE "Do not hardcode the poppler library location (on Windows)." ON) else() @@ -112,21 +118,23 @@ endif() if(WIN32) set(_default_fontconfiguration "win32") elseif(ANDROID) - # on android we don't have fontconfig and we don't want windows-specific code - set(_default_fontconfiguration "generic") + set(_default_fontconfiguration "android") else() set(_default_fontconfiguration "fontconfig") endif() -set(FONT_CONFIGURATION "${_default_fontconfiguration}" CACHE STRING "The font configuration backend (win32|generic|fontconfig).") +set(FONT_CONFIGURATION "${_default_fontconfiguration}" CACHE STRING "The font configuration backend (win32|android|generic|fontconfig).") string(TOLOWER "${FONT_CONFIGURATION}" font_configuration) set(WITH_FONTCONFIGURATION_WIN32 OFF) set(WITH_FONTCONFIGURATION_FONTCONFIG OFF) +set(WITH_FONTCONFIGURATION_ANDROID OFF) if(font_configuration STREQUAL "win32") set(WITH_FONTCONFIGURATION_WIN32 ON) elseif(font_configuration STREQUAL "fontconfig") set(WITH_FONTCONFIGURATION_FONTCONFIG ON) +elseif(font_configuration STREQUAL "android") + set(WITH_FONTCONFIGURATION_ANDROID ON) elseif(font_configuration STREQUAL "generic") - message(STATUS "no fontconfig or win32 specific code") + message(STATUS "no win32, android, or fontconfig specific code") else() message(FATAL_ERROR "Invalid font configuration setting: ${FONT_CONFIGURATION}") endif() @@ -136,22 +144,54 @@ set(OPI_SUPPORT ON) set(TEXTOUT_WORD_LIST ON) # setting the minimum required versions for some components -set(CAIRO_VERSION "1.10.0") -set(GLIB_REQUIRED "2.56") -set(GTK_REQUIRED "3.22") -set(GDK_PIXBUF_REQUIRED "2.36") -set(FREETYPE_VERSION "2.8") +set(CAIRO_VERSION "1.16.0") +set(GLIB_REQUIRED "2.72") +set(GTK_REQUIRED "3.24") +set(GDK_PIXBUF_REQUIRED "2.40") +set(FREETYPE_VERSION "2.11") +set(FONTCONFIG_VERSION "2.13") find_package(Freetype ${FREETYPE_VERSION} REQUIRED) if(WITH_FONTCONFIGURATION_FONTCONFIG) - find_package(Fontconfig REQUIRED) + find_package(Fontconfig ${FONTCONFIG_VERSION} REQUIRED) +elseif(WITH_FONTCONFIGURATION_ANDROID) + find_library(Androidlib NAMES android REQUIRED) endif() + +macro(find_soft_mandatory_package _enable_option _package_name _package_version) + if(${_enable_option}) + find_package(${_package_name} ${_package_version}) + if(NOT ${_package_name}_FOUND) + MESSAGE(FATAL_ERROR "Could not find the ${_package_version} version of ${_package_name}. If you're not interested in the features it provides set the cmake ${_enable_option} option to OFF") + endif() + endif() +endmacro() + +find_soft_mandatory_package(ENABLE_NSS3 NSS3 3.68) +find_soft_mandatory_package(ENABLE_GPGME Gpgmepp 1.19) +find_soft_mandatory_package(ENABLE_LIBTIFF TIFF 4.3) + macro_optional_find_package(JPEG) macro_optional_find_package(PNG) -macro_optional_find_package(TIFF) -macro_optional_find_package(NSS3) if(ENABLE_DCTDECODER STREQUAL "libjpeg") if(JPEG_FOUND) + include(CheckCSourceCompiles) + set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") + set(CMAKE_REQUIRED_LIBRARIES JPEG::JPEG) + check_c_source_compiles(" + #include + #include + #include + int main() { struct jpeg_decompress_struct info; jpeg_mem_src(&info, 0, 0); return 0; }" HAVE_JPEG_MEM_SRC) + set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}") + + if(NOT HAVE_JPEG_MEM_SRC) + message(FATAL_ERROR "Your libjpeg is too old. Poppler needs one that provides jpeg_mem_src. That is provided in libjpeg >= 8 or libjpeg-turbo >= 1.1.0. You can \ +also decide to use the internal unmaintained DCT decoder or none at all.\n\ +Possible options are: -DENABLE_DCTDECODER=libjpeg, -DENABLE_DCTDECODER=none, \ +-DENABLE_DCTDECODER=unmaintained") + endif() + set(ENABLE_LIBJPEG ${JPEG_FOUND}) else() message(STATUS "Could NOT find libjpeg.") @@ -171,31 +211,24 @@ else() message(FATAL_ERROR "Invalid ENABLE_DCTDECODER value.") endif() -if (ENABLE_QT5) - find_package(Qt5Core 5.9) # Update QT_DISABLE_DEPRECATED_BEFORE in qt5/CMakeLists.txt when increasing this - find_package(Qt5Gui) - find_package(Qt5Xml) - find_package(Qt5Widgets) - find_package(Qt5Test) - if (NOT (Qt5Core_FOUND AND Qt5Gui_FOUND AND Qt5Xml_FOUND AND Qt5Widgets_FOUND AND Qt5Test_FOUND)) - message("-- Package Qt5Core or Qt5Gui or Qt5Xml or Qt5Widgets or Qt5Test not found") - set(ENABLE_QT5 OFF) - endif() -endif() +set(QT5_VERSION "5.15") +find_soft_mandatory_package(ENABLE_QT5 Qt5Core ${QT5_VERSION}) +find_soft_mandatory_package(ENABLE_QT5 Qt5Gui ${QT5_VERSION}) +find_soft_mandatory_package(ENABLE_QT5 Qt5Xml ${QT5_VERSION}) +find_soft_mandatory_package(ENABLE_QT5 Qt5Widgets ${QT5_VERSION}) +find_soft_mandatory_package(ENABLE_QT5 Qt5Test ${QT5_VERSION}) -if (ENABLE_QT6) - SET(QT_NO_CREATE_VERSIONLESS_TARGETS ON) - find_package(Qt6 6.2 COMPONENTS Core Gui Widgets Test QUIET) - if (NOT (Qt6Core_FOUND AND Qt6Gui_FOUND AND Qt6Widgets_FOUND AND Qt6Test_FOUND)) - message("-- Package Qt6Core or Qt6Gui or Qt6Widgets or Qt6Test not found") - set(ENABLE_QT6 OFF) - endif() -endif() +set(QT6_VERSION "6.2") +SET(QT_NO_CREATE_VERSIONLESS_TARGETS ON) +find_soft_mandatory_package(ENABLE_QT6 Qt6Core ${QT6_VERSION}) +find_soft_mandatory_package(ENABLE_QT6 Qt6Gui ${QT6_VERSION}) +find_soft_mandatory_package(ENABLE_QT6 Qt6Widgets ${QT6_VERSION}) +find_soft_mandatory_package(ENABLE_QT6 Qt6Test ${QT6_VERSION}) # Check for Cairo rendering backend macro_optional_find_package(Cairo ${CAIRO_VERSION}) -find_package(Boost 1.58.0) +find_package(Boost 1.74.0 CONFIG) if(Boost_FOUND) set(USE_BOOST_HEADERS ON) elseif(ENABLE_BOOST) @@ -217,7 +250,7 @@ if(CAIRO_FOUND) if(ENABLE_GLIB) if(ENABLE_GOBJECT_INTROSPECTION) # Check for introspection - macro_optional_find_package(GObjectIntrospection 0.9.12) + macro_optional_find_package(GObjectIntrospection 1.72.0) set(HAVE_INTROSPECTION ${INTROSPECTION_FOUND}) endif() set(POPPLER_GLIB_DISABLE_DEPRECATED "${POPPLER_GLIB_DISABLE_DEPRECATED} -DG_DISABLE_DEPRECATED") @@ -245,16 +278,28 @@ if(ENABLE_GTK_DOC) endif() if(ENABLE_CPP) + cmake_push_check_state() find_package(Iconv REQUIRED) + set(CMAKE_REQUIRED_LIBRARIES Iconv::Iconv) + check_cxx_source_compiles(" + #include + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } + " ICONV_SECOND_ARGUMENT_IS_CONST) + cmake_pop_check_state() + if(ICONV_SECOND_ARGUMENT_IS_CONST) + set(ICONV_CONST "const") + endif() endif() -if(ENABLE_ZLIB) - find_package(ZLIB) - set(ENABLE_ZLIB ${ZLIB_FOUND}) -endif() -if(ENABLE_ZLIB_UNCOMPRESS AND NOT ENABLE_ZLIB) - message("Warning: ENABLE_ZLIB_UNCOMPRESS requires ENABLE_ZLIB") - set(ENABLE_ZLIB_UNCOMPRESS FALSE) -endif() +find_package(ZLIB REQUIRED) + set(WITH_OPENJPEG FALSE) if(ENABLE_LIBOPENJPEG STREQUAL "openjpeg2") find_package(OpenJPEG) @@ -277,20 +322,12 @@ else() message(FATAL_ERROR "Invalid ENABLE_LIBOPENJPEG value: ${ENABLE_LIBOPENJPEG}") endif() set(ENABLE_LIBOPENJPEG "${WITH_OPENJPEG}") -if(ENABLE_CMS STREQUAL "lcms2") - find_package(LCMS2) - set(USE_CMS ${LCMS2_FOUND}) -elseif(NOT ENABLE_CMS STREQUAL "none") - message(FATAL_ERROR "Invalid ENABLE_CMS value: ${ENABLE_CMS}") -endif() -if(ENABLE_LIBCURL) - find_package(CURL) - if(CURL_FOUND) - set(POPPLER_HAS_CURL_SUPPORT ON) - else() - set(ENABLE_LIBCURL OFF) - endif() -endif() + +find_soft_mandatory_package(ENABLE_LCMS LCMS2 2.12) +set(USE_CMS ${ENABLE_LCMS}) + +find_soft_mandatory_package(ENABLE_LIBCURL CURL 7.81) +set(POPPLER_HAS_CURL_SUPPORT ${ENABLE_LIBCURL}) if(MINGW) # Use mingw's ansi stdio extensions @@ -302,23 +339,30 @@ if(WITH_FONTCONFIGURATION_WIN32) add_definitions(-D_WIN32_IE=0x0500) endif() endif() -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/fofi - ${CMAKE_CURRENT_SOURCE_DIR}/goo - ${CMAKE_CURRENT_SOURCE_DIR}/poppler - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/poppler -) -if (NSS3_FOUND) - set(ENABLE_NSS3 ON) -endif() if(PNG_FOUND) set(ENABLE_LIBPNG ON) endif() -if(TIFF_FOUND) - set(ENABLE_LIBTIFF ON) + +set(SIGNATURE_BACKENDS "") +if(ENABLE_NSS3) + list(APPEND SIGNATURE_BACKENDS "NSS") +endif() +if(ENABLE_GPGME) + list(APPEND SIGNATURE_BACKENDS "GPG") +endif() + +list(LENGTH SIGNATURE_BACKENDS _signing_backends_count) +if (_signing_backends_count GREATER 0) + if (NOT DEFAULT_SIGNATURE_BACKEND) + # If not specified at compiletime, we take the first one added. + # This means that the order we append them to the list is significant + list(GET SIGNATURE_BACKENDS 0 DEFAULT_SIGNATURE_BACKEND) + endif() + set(ENABLE_SIGNATURES ON) +endif() +if (NOT DEFAULT_SIGNATURE_BACKEND) + set(DEFAULT_SIGNATURE_BACKEND "None") endif() # Recent versions of poppler-data install a .pc file. @@ -381,6 +425,7 @@ set(poppler_SRCS poppler/Catalog.cc poppler/CharCodeToUnicode.cc poppler/CMap.cc + poppler/CryptoSignBackend.cc poppler/DateInfo.cc poppler/Decrypt.cc poppler/Dict.cc @@ -388,6 +433,7 @@ set(poppler_SRCS poppler/FDPDFDocBuilder.cc poppler/FILECacheLoader.cc poppler/FileSpec.cc + poppler/FlateEncoder.cc poppler/FontEncodingTables.cc poppler/Form.cc poppler/FontInfo.cc @@ -458,29 +504,20 @@ set(poppler_SRCS splash/SplashXPath.cc splash/SplashXPathScanner.cc ) -set(poppler_LIBS Freetype::Freetype) +set(poppler_LIBS Freetype::Freetype ZLIB::ZLIB) if(FONTCONFIG_FOUND) - add_definitions(${FONTCONFIG_DEFINITIONS}) - include_directories(SYSTEM ${FONTCONFIG_INCLUDE_DIR}) - set(poppler_LIBS ${poppler_LIBS} ${FONTCONFIG_LIBRARIES}) + set(poppler_LIBS ${poppler_LIBS} Fontconfig::Fontconfig) endif() -if(JPEG_FOUND) - set(poppler_SRCS ${poppler_SRCS} - poppler/DCTStream.cc - ) - if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - include_directories(SYSTEM ${JPEG_INCLUDE_DIR}) - set(poppler_LIBS ${poppler_LIBS} ${JPEG_LIBRARIES}) - else() - set(poppler_LIBS ${poppler_LIBS} JPEG::JPEG) - endif() +if(Androidlib) + set(poppler_LIBS ${poppler_LIBS} ${Androidlib}) endif() -if(ENABLE_ZLIB) + +if(ENABLE_LIBJPEG) set(poppler_SRCS ${poppler_SRCS} - poppler/FlateEncoder.cc + poppler/DCTStream.cc ) - set(poppler_LIBS ${poppler_LIBS} ZLIB::ZLIB) + set(poppler_LIBS ${poppler_LIBS} JPEG::JPEG) endif() if(ENABLE_ZLIB_UNCOMPRESS) set(poppler_SRCS ${poppler_SRCS} @@ -492,33 +529,31 @@ if(ENABLE_LIBCURL) poppler/CurlCachedFile.cc poppler/CurlPDFDocBuilder.cc ) - if(${CMAKE_VERSION} VERSION_LESS "3.12.0") - include_directories(SYSTEM ${CURL_INCLUDE_DIRS}) - set(poppler_LIBS ${poppler_LIBS} ${CURL_LIBRARIES}) - else() - set(poppler_LIBS ${poppler_LIBS} CURL::libcurl) - endif() + set(poppler_LIBS ${poppler_LIBS} CURL::libcurl) endif() if (ENABLE_NSS3) set(poppler_SRCS ${poppler_SRCS} - poppler/SignatureHandler.cc + poppler/NSSCryptoSignBackend.cc ) set(poppler_LIBS ${poppler_LIBS} PkgConfig::NSS3) endif() +if (ENABLE_GPGME) + set(poppler_SRCS ${poppler_SRCS} + poppler/GPGMECryptoSignBackend.cc + ) + set(poppler_LIBS ${poppler_LIBS} Gpgmepp) +endif() if (OpenJPEG_FOUND) set(poppler_SRCS ${poppler_SRCS} poppler/JPEG2000Stream.cc ) set(poppler_LIBS ${poppler_LIBS} openjp2) - # target openjp2 may lack interface include directories - include_directories(SYSTEM ${OPENJPEG_INCLUDE_DIRS}) else () set(poppler_SRCS ${poppler_SRCS} poppler/JPXStream.cc ) endif() if(USE_CMS) - include_directories(SYSTEM ${LCMS2_INCLUDE_DIR}) set(poppler_LIBS ${poppler_LIBS} ${LCMS2_LIBRARIES}) endif() if(WIN32) @@ -530,7 +565,7 @@ endif() if(PNG_FOUND) set(poppler_LIBS ${poppler_LIBS} PNG::PNG) endif() -if(TIFF_FOUND) +if(ENABLE_LIBTIFF) set(poppler_LIBS ${poppler_LIBS} TIFF::TIFF) endif() if(Boost_FOUND) @@ -574,12 +609,52 @@ ADD_GPERF_FILE(TimesItalicWidths) ADD_GPERF_FILE(TimesRomanWidths) ADD_GPERF_FILE(ZapfDingbatsWidths) +set(POPPLER_SOVERSION_NUMBER "147") + +set(LINKER_SCRIPT "${CMAKE_BINARY_DIR}/libpoppler.map") +configure_file( + "${CMAKE_SOURCE_DIR}/poppler/libpoppler.map.in" + ${LINKER_SCRIPT}) + if(MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif() -add_library(poppler ${poppler_SRCS}) +add_library(poppler ${poppler_SRCS} ${LINKER_SCRIPT}) +target_include_directories(poppler PUBLIC poppler ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/poppler) +if (OpenJPEG_FOUND) + # check if we can remove this when we depend on newer openjpeg versions, 2.5 seems fixed + # target openjp2 may lack interface include directories + target_include_directories(poppler SYSTEM PRIVATE ${OPENJPEG_INCLUDE_DIRS}) +endif() +if(USE_CMS) + target_include_directories(poppler SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) +endif() generate_export_header(poppler BASE_NAME poppler-private EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/poppler_private_export.h") -set_target_properties(poppler PROPERTIES VERSION 126.0.0 SOVERSION 126) +set_target_properties(poppler PROPERTIES + VERSION ${POPPLER_SOVERSION_NUMBER}.0.0 + SOVERSION ${POPPLER_SOVERSION_NUMBER}) + +if(UNIX AND (NOT APPLE)) + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/linkerscript_test.map "VERS_1 {\nglobal:\n *;};\n") + + # once we require cmake 3.18, + # the next set of lines can be changed + # to the check_linker_flags function instead + include(CheckCXXSourceCompiles) + set(OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} -Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/linkerscript_test.map) + check_cxx_source_compiles(" + int main(int, char**) { + return 0; + } + " SUPPORT_VERSION_SCRIPT) + set(CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) + + file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/linkerscript_test.map) + if (SUPPORT_VERSION_SCRIPT) + set_target_properties(poppler PROPERTIES LINK_OPTIONS LINKER:--version-script=${LINKER_SCRIPT}) + endif() +endif() if(MINGW AND BUILD_SHARED_LIBS) get_target_property(POPPLER_SOVERSION poppler SOVERSION) set_target_properties(poppler PROPERTIES SUFFIX "-${POPPLER_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") @@ -588,22 +663,18 @@ target_link_libraries(poppler LINK_PRIVATE ${poppler_LIBS}) install(TARGETS poppler RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(ENABLE_UNSTABLE_API_ABI_HEADERS) - install(FILES + set(poppler_poppler_installed_headers poppler/Annot.h poppler/AnnotStampImageHelper.h poppler/Array.h poppler/CachedFile.h poppler/Catalog.h - poppler/CharCodeToUnicode.h - poppler/CMap.h + poppler/CryptoSignBackend.h poppler/DateInfo.h - poppler/Decrypt.h poppler/Dict.h poppler/Error.h - poppler/FDPDFDocBuilder.h poppler/FILECacheLoader.h poppler/FileSpec.h - poppler/FontEncodingTables.h poppler/FontInfo.h poppler/Form.h poppler/Function.h @@ -612,17 +683,12 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS) poppler/GfxState.h poppler/GfxState_helpers.h poppler/GlobalParams.h - poppler/Hints.h - poppler/JArithmeticDecoder.h - poppler/JBIG2Stream.h + poppler/HashAlgorithm.h poppler/JSInfo.h poppler/Lexer.h poppler/Link.h - poppler/Linearization.h - poppler/LocalPDFDocBuilder.h poppler/MarkedContentOutputDev.h poppler/Movie.h - poppler/NameToCharCode.h poppler/Object.h poppler/OptionalContent.h poppler/Outline.h @@ -636,10 +702,9 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS) poppler/PDFDocFactory.h poppler/PopplerCache.h poppler/ProfileData.h - poppler/PreScanOutputDev.h - poppler/PSTokenizer.h poppler/Rendition.h poppler/CertificateInfo.h + poppler/SignatureInfo.h poppler/Stream-CCITT.h poppler/Stream.h poppler/StructElement.h @@ -658,72 +723,34 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS) poppler/NameToUnicodeTable.h poppler/PSOutputDev.h poppler/TextOutputDev.h - poppler/SecurityHandler.h poppler/BBoxOutputDev.h poppler/UTF.h poppler/Sound.h - ${CMAKE_CURRENT_BINARY_DIR}/poppler_private_export.h - ${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h poppler/SplashOutputDev.h - DESTINATION include/poppler) - install(FILES + ) + set(poppler_goo_installed_headers goo/GooTimer.h goo/GooString.h goo/gmem.h - goo/gdir.h goo/gfile.h goo/ImgWriter.h goo/GooCheckedOps.h goo/GooLikely.h goo/gstrtod.h goo/grandom.h - DESTINATION include/poppler/goo) - if(PNG_FOUND) - install(FILES - goo/PNGWriter.h - DESTINATION include/poppler/goo) - endif() - if(TIFF_FOUND) - install(FILES - goo/TiffWriter.h - DESTINATION include/poppler/goo) - endif() - if(JPEG_FOUND) - install(FILES - goo/JpegWriter.h - DESTINATION include/poppler/goo) - endif() - install(FILES + ) + set(poppler_fofi_installed_headers fofi/FoFiBase.h fofi/FoFiEncodings.h fofi/FoFiTrueType.h - fofi/FoFiType1.h fofi/FoFiType1C.h fofi/FoFiIdentifier.h - DESTINATION include/poppler/fofi) - if(ENABLE_LIBCURL) - install(FILES - poppler/CurlCachedFile.h - poppler/CurlPDFDocBuilder.h - DESTINATION include/poppler) - endif() - if(OpenJPEG_FOUND) - install(FILES - poppler/JPEG2000Stream.h - DESTINATION include/poppler) - else() - install(FILES - poppler/JPXStream.h - DESTINATION include/poppler) - endif() - install(FILES + ) + set(poppler_splash_installed_headers splash/Splash.h splash/SplashBitmap.h splash/SplashClip.h splash/SplashErrorCodes.h - splash/SplashFTFont.h - splash/SplashFTFontEngine.h - splash/SplashFTFontFile.h splash/SplashFont.h splash/SplashFontEngine.h splash/SplashFontFile.h @@ -732,21 +759,104 @@ if(ENABLE_UNSTABLE_API_ABI_HEADERS) splash/SplashMath.h splash/SplashPath.h splash/SplashPattern.h - splash/SplashScreen.h - splash/SplashState.h splash/SplashTypes.h - splash/SplashXPath.h - splash/SplashXPathScanner.h - DESTINATION include/poppler/splash) - if(CAIRO_FOUND) + ) + if(ENABLE_LIBCURL) + set(poppler_poppler_installed_headers + ${poppler_poppler_installed_headers} + poppler/CurlCachedFile.h + poppler/CurlPDFDocBuilder.h) + endif() + if(OpenJPEG_FOUND) + set(poppler_poppler_installed_headers ${poppler_poppler_installed_headers} poppler/JPEG2000Stream.h) + else() + set(poppler_poppler_installed_headers ${poppler_poppler_installed_headers} poppler/JPXStream.h) + endif() + if(PNG_FOUND) + set(poppler_goo_installed_headers ${poppler_goo_installed_headers} goo/PNGWriter.h) + endif() + if(ENABLE_LIBTIFF) + set(poppler_goo_installed_headers ${poppler_goo_installed_headers} goo/TiffWriter.h) + endif() + if(JPEG_FOUND) + set(poppler_goo_installed_headers ${poppler_goo_installed_headers} goo/JpegWriter.h) + endif() + + if (${CMAKE_VERSION} VERSION_LESS "3.23.0") install(FILES - poppler/CairoFontEngine.h - poppler/CairoOutputDev.h - poppler/CairoRescaleBox.h - DESTINATION include/poppler) + ${poppler_poppler_installed_headers} + ${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h + ${CMAKE_CURRENT_BINARY_DIR}/poppler_private_export.h + DESTINATION include/poppler) + install(FILES ${poppler_goo_installed_headers} DESTINATION include/poppler/goo) + install(FILES ${poppler_fofi_installed_headers} DESTINATION include/poppler/fofi) + install(FILES ${poppler_splash_installed_headers} DESTINATION include/poppler/splash) + else() + target_sources(poppler + PUBLIC FILE_SET poppler_poppler + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/poppler + FILES ${poppler_poppler_installed_headers} + ) + target_sources(poppler + PUBLIC FILE_SET poppler_poppler_config + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/poppler + FILES ${CMAKE_CURRENT_BINARY_DIR}/poppler/poppler-config.h + ) + target_sources(poppler + PUBLIC FILE_SET poppler_export + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} + FILES ${CMAKE_CURRENT_BINARY_DIR}/poppler_private_export.h + ) + target_sources(poppler + PUBLIC FILE_SET poppler_goo_fofi_splash + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + FILES + ${poppler_goo_installed_headers} + ${poppler_fofi_installed_headers} + ${poppler_splash_installed_headers} + ) + install(TARGETS poppler + FILE_SET poppler_poppler DESTINATION include/poppler + FILE_SET poppler_export DESTINATION include/poppler + FILE_SET poppler_poppler_config DESTINATION include/poppler + FILE_SET poppler_goo_fofi_splash DESTINATION include/poppler + ) endif() endif() +function(VERIFY_PUBLIC_HEADERS target subfolder) + set(_gen_source_files "") + foreach(header_file ${ARGN}) + get_filename_component(_name ${header_file} NAME_WLE) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/${target}/${_name}.cpp" + "#include \"${header_file}\"") + file(COPY "${header_file}" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/${subfolder}/${target}/") + list(APPEND _gen_source_files + "${CMAKE_CURRENT_BINARY_DIR}/public_if_test/${target}/${_name}.cpp") + endforeach() + add_library(test_public_interface_${target} MODULE ${_gen_source_files}) + target_include_directories(test_public_interface_${target} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/poppler + ${CMAKE_CURRENT_BINARY_DIR}/public_if_test/goo + ${CMAKE_CURRENT_BINARY_DIR}/public_if_test/fofi + ${CMAKE_CURRENT_BINARY_DIR}/public_if_test/splash + ${CMAKE_CURRENT_BINARY_DIR}/public_if_test/poppler/poppler + ) + add_dependencies(poppler test_public_interface_${target}) +endfunction() +# Create a build target to check if the '*_public.h' files are self contained +if(UNIX AND VERIFY_PUBLIC_PRIVATE_HEADERS) + VERIFY_PUBLIC_HEADERS(goo goo ${poppler_goo_installed_headers}) + VERIFY_PUBLIC_HEADERS(fofi fofi ${poppler_fofi_installed_headers}) + VERIFY_PUBLIC_HEADERS(splash splash ${poppler_splash_installed_headers}) + VERIFY_PUBLIC_HEADERS(poppler poppler ${poppler_poppler_installed_headers}) +endif() if(ENABLE_UTILS) add_subdirectory(utils) @@ -803,9 +913,11 @@ show_end_message_yesno("cpp wrapper" ENABLE_CPP) show_end_message_yesno("use libjpeg" ENABLE_LIBJPEG) show_end_message_yesno("use libpng" ENABLE_LIBPNG) show_end_message_yesno("use libtiff" ENABLE_LIBTIFF) -show_end_message_yesno("use zlib compress" ENABLE_ZLIB) show_end_message_yesno("use zlib uncompress" ENABLE_ZLIB_UNCOMPRESS) show_end_message_yesno("use nss3" ENABLE_NSS3) +show_end_message_yesno("use gpg" ENABLE_GPGME) +show_end_message(" default signature backend" ${DEFAULT_SIGNATURE_BACKEND}) +show_end_message_yesno(" enable pgp signatures in gpg backend by default" ENABLE_PGP_SIGNATURES) show_end_message_yesno("use curl" ENABLE_LIBCURL) show_end_message_yesno("use libopenjpeg2" WITH_OPENJPEG) show_end_message_yesno("use lcms2" USE_CMS) diff --git a/ChangeLog b/ChangeLog index ffddaca..dfeb2c7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9969 @@ +commit 7b620e866b6729ceffb5ecb0298e9acc968e1c1f +Author: Albert Astals Cid +Date: Mon Mar 3 23:03:20 2025 +0100 + + poppler 25.03.0 + + CMakeLists.txt | 6 +++--- + NEWS | 13 +++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 21 insertions(+), 8 deletions(-) + +commit 828d139b4830eaa97447fefb40a943f0b761c045 +Author: Albert Astals Cid +Date: Mon Mar 3 00:26:26 2025 +0100 + + Update (C) + + poppler/Page.cc | 2 +- + utils/ImageOutputDev.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit b91ce32ba7d3afbfc1ead01baabb21260829c358 +Author: Albert Astals Cid +Date: Sun Mar 2 11:58:59 2025 +0100 + + Set aOidData.oid.type + + Otherwise later + aAttribute.type = aOidData.oid; + ends up being a copy of unused memory + + siBuffer is what the old code removed in + 84f5268256b40d40f12fa2290845b607d7895529 used + + Also remove an + aOidData.oid.data = nullptr; + that does nothing since it gets overwritten 2 lines later + + poppler/NSSCryptoSignBackend.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit e78f4c937e1e94912b8b175a8e8e8ff2ef52ace3 +Author: Albert Astals Cid +Date: Sun Mar 2 11:41:27 2025 +0100 + + Fix memory leaks introduced in the str->reset commit + + poppler/PSOutputDev.cc | 9 ++++----- + poppler/Page.cc | 4 ++-- + utils/ImageOutputDev.cc | 16 ++++++++-------- + 3 files changed, 14 insertions(+), 15 deletions(-) + +commit 70b80793218e0ce0c029ea245832f1843accb7a6 +Author: Albert Astals Cid +Date: Thu Feb 27 19:32:40 2025 +0100 + + Update (C) + + qt6/tests/check_signature_basics.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit a45449bedbc74e4f0f0998fd23e636ce331f858c +Author: Sune Vuorela +Date: Thu Feb 27 14:21:04 2025 +0100 + + fix pgp test with no signature backends + + qt6/tests/check_signature_basics.cpp | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit bb58c2ee2bc560d7e5bf65a2d840192278cb354f +Author: Sune Vuorela +Date: Thu Feb 27 12:36:58 2025 +0100 + + Fix windows; include array + + poppler/GPGMECryptoSignBackend.cc | 1 + + 1 file changed, 1 insertion(+) + +commit d3b96a62acd388ffe45dff65e517cd5726c147ba +Author: Sune Vuorela +Date: Thu Feb 13 20:06:27 2025 +0100 + + Add unit test + + qt6/tests/CMakeLists.txt | 3 + + qt6/tests/check_signature_basics.cpp | 144 + +++++++++++++++++++++++++++++++++++ + 2 files changed, 147 insertions(+) + +commit f0316ba62df9ce6d8e17a9349934b4a06df87e69 +Author: Sune Vuorela +Date: Mon Dec 2 16:36:20 2024 +0100 + + Put a pgp signature in a pdf file + + Allow putting a pgp signature in a pdf file. + This is not fully documented inside the specification, but basically + it + is replacing the CMS bundle as described with a pgp bundle. + + These signatures are tagged in the g10c namespace + (https://github.com/adobe/pdf-names-list/issues/45) and is only + implemented in the GPGME backend as a 'whatever gpg understands'. + + A special difference is that the signature is not padded with zeroes, + but with a gpg comment packet with the exact size to fill the void. + + CMakeLists.txt | 2 + + config.h.cmake | 3 + + poppler/CertificateInfo.cc | 10 +++ + poppler/CertificateInfo.h | 11 ++- + poppler/CryptoSignBackend.cc | 4 + + poppler/CryptoSignBackend.h | 1 + + poppler/Form.cc | 106 ++++++++++++++----------- + poppler/GPGMECryptoSignBackend.cc | 159 + +++++++++++++++++++++++++++++--------- + poppler/GPGMECryptoSignBackend.h | 17 +++- + poppler/NSSCryptoSignBackend.cc | 1 + + qt5/src/poppler-form.cc | 43 +++++++++++ + qt5/src/poppler-form.h | 38 ++++++++- + qt6/src/poppler-form.cc | 44 +++++++++++ + qt6/src/poppler-form.h | 38 ++++++++- + utils/pdfsig.cc | 3 + + 15 files changed, 391 insertions(+), 89 deletions(-) + +commit 287cd3aa9423ba2a038462f309d10093a8fbccbd +Author: Sune Vuorela +Date: Thu Feb 27 11:21:29 2025 +0100 + + Fix windows run + + poppler/PDFDoc.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 9ce35a2cd89489d91c2d754c2c67d837f44875c7 +Author: Albert Astals Cid +Date: Wed Feb 26 22:07:39 2025 +0100 + + Update (C) + + glib/poppler-annot.cc | 1 + + glib/poppler-annot.h | 1 + + glib/poppler-structure-element.cc | 1 + + poppler/CharCodeToUnicode.cc | 2 +- + poppler/CryptoSignBackend.h | 2 +- + poppler/DCTStream.h | 2 +- + poppler/GPGMECryptoSignBackend.cc | 2 +- + poppler/JPEG2000Stream.cc | 1 + + poppler/JSInfo.cc | 2 +- + poppler/JSInfo.h | 1 + + poppler/MarkedContentOutputDev.cc | 1 + + poppler/MarkedContentOutputDev.h | 1 + + poppler/PDFDoc.cc | 2 +- + poppler/StructElement.cc | 1 + + poppler/StructElement.h | 1 + + qt6/src/poppler-annotation.h | 2 +- + qt6/src/poppler-form.h | 2 +- + 17 files changed, 17 insertions(+), 8 deletions(-) + +commit 84f5268256b40d40f12fa2290845b607d7895529 +Author: Juraj Šarinay +Date: Tue Jan 21 08:58:22 2025 +0100 + + Remove my_SEC_StringToOID(), replace the string by an OID. + + The function was only ever called on string representation + of id-aa-signingCertificateV2. Simplify the code by using + the corresponding OID directly and remove the function. + + poppler/NSSCryptoSignBackend.cc | 131 + +++++++++------------------------------- + 1 file changed, 28 insertions(+), 103 deletions(-) + +commit 08c2f603c2f5758a929862b7da8a1105ee5d591b +Author: Markus Göllnitz +Date: Mon Feb 24 06:24:30 2025 +0100 + + glib: check file attachment annotation for this class before casting + + The check was correct, but not sufficient and could lead + to segmentation violations. + + Signed-off-by: Markus Göllnitz + + glib/poppler-annot.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit dac9e5254fbc24cdaefcaca734ef07bd2eaf8e62 +Author: Markus Göllnitz +Date: Mon Feb 24 06:21:51 2025 +0100 + + glib: subclass stamp as markup + + AnnotStamp already sublasses AnnotMarkup in + poppler/Annot.cc, but this was never exposed to GLib. + + Signed-off-by: Markus Göllnitz + + glib/poppler-annot.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit cc2897f016e269956ba0b7e2f50971d5cdefdd03 +Author: Juraj Šarinay +Date: Mon Feb 17 00:05:44 2025 +0100 + + Do not embed unnecessary fonts if signature appearance string + is empty. + + poppler/Form.cc | 45 ++++++++++++++++++++++++--------------------- + poppler/PDFDoc.cc | 25 ++++++++++++++----------- + 2 files changed, 38 insertions(+), 32 deletions(-) + +commit ccef623296cf491dea5effc8242053846d5acbcc +Author: Sune Vuorela +Date: Thu Feb 20 11:01:58 2025 +0100 + + Less goostring pointers + + Stream formats can be std::string, and a few other pointers aren't + needed + + poppler/Annot.cc | 21 +++++--------- + poppler/Annot.h | 2 +- + poppler/CairoOutputDev.cc | 13 ++++----- + poppler/DCTStream.cc | 10 +++---- + poppler/DCTStream.h | 2 +- + poppler/FlateEncoder.h | 2 +- + poppler/Form.cc | 10 ++----- + poppler/Form.h | 2 +- + poppler/JBIG2Stream.cc | 4 +-- + poppler/JBIG2Stream.h | 2 +- + poppler/JPEG2000Stream.cc | 4 +-- + poppler/JPEG2000Stream.h | 2 +- + poppler/JPXStream.cc | 4 +-- + poppler/JPXStream.h | 2 +- + poppler/Object.h | 5 ++-- + poppler/PDFDoc.cc | 23 +++++---------- + poppler/PDFDoc.h | 4 +-- + poppler/PSOutputDev.cc | 25 +++++++--------- + poppler/Stream.cc | 74 + +++++++++++++++++++++++------------------------ + poppler/Stream.h | 37 ++++++++++++------------ + poppler/StructElement.cc | 15 ++++------ + poppler/StructElement.h | 5 ++-- + utils/pdfinfo.cc | 4 +-- + 23 files changed, 119 insertions(+), 153 deletions(-) + +commit feedc7bb40905b0e4aced1b9fea984e126db370e +Author: Sune Vuorela +Date: Tue Feb 11 10:52:17 2025 +0100 + + Signature-GPGME: more correct validation handling + + Previously the code used the slightly higher-level api that tried to + combine signature and certificate validation into one. + + Given poppler have the certificate validation as a different step, use + just the signature valditaion api for signature validation api. + + poppler/GPGMECryptoSignBackend.cc | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +commit 0f8a128fd3f3ae5400e78cd714ced04ddb090637 +Author: Sune Vuorela +Date: Wed Feb 19 14:58:38 2025 +0100 + + Less manual new/delete of GooString + + * Replace a few with std::string + * Microoptimization: fewer string compares + * Replace manual refcount with shared_ptr + * Do a couple of std::string_view + + glib/poppler-structure-element.cc | 2 +- + poppler/Catalog.cc | 15 +++++------ + poppler/Catalog.h | 2 +- + poppler/CharCodeToUnicode.cc | 8 +++--- + poppler/JSInfo.cc | 16 +++++------- + poppler/JSInfo.h | 2 +- + poppler/MarkedContentOutputDev.cc | 11 +++----- + poppler/MarkedContentOutputDev.h | 39 ++++++++-------------------- + poppler/PDFDoc.cc | 54 + ++++++++++++++++++++++++--------------- + poppler/UTF.cc | 9 +++---- + poppler/UTF.h | 8 +++--- + qt5/src/poppler-document.cc | 5 ++-- + qt6/src/poppler-document.cc | 5 ++-- + 13 files changed, 80 insertions(+), 96 deletions(-) + +commit 8f465fea3778e233eb11d1ac22e56cbe5ac722a1 +Author: Sune Vuorela +Date: Wed Feb 12 14:46:48 2025 +0100 + + Signing: Improve error handling on bad passphrase + + poppler/CryptoSignBackend.h | 1 + + poppler/GPGMECryptoSignBackend.cc | 6 ++++++ + qt6/src/poppler-annotation.cc | 2 ++ + qt6/src/poppler-annotation.h | 1 + + qt6/src/poppler-form.cc | 2 ++ + qt6/src/poppler-form.h | 4 ++++ + 6 files changed, 16 insertions(+) + +commit 7d1b09326e4994d0382153d09c448a9df62e24ae +Author: Albert Astals Cid +Date: Wed Feb 19 22:52:10 2025 +0100 + + Update (C) + + glib/poppler-form-field.cc | 1 + + poppler/Catalog.cc | 1 + + 2 files changed, 2 insertions(+) + +commit 8ffae6f9720fe11ff646461677ce4b532dfd3b2a +Author: Aaron Nguyen +Date: Wed Feb 19 20:59:22 2025 +0000 + + Handle bad catalog dictionaries where Pages points to its child + + This should take care of edge cases like Issue #1559 where we get + errors like `Syntax Error: Kids object (page 1) is wrong type (null)` + in scenarios where there are incorrect Pages value in its catalog + dictionary. In the case of `badcatalogpages.pdf` in Issue #1559, the + Pages value `/Pages 14 0 R` actually points to its Kid, which is a + Page value. Here is the PDF content stream for `badcatalogpages.pdf`: + + ``` + 12 0 obj + <>/Metadata 9 0 R/Pages 14 + 0 R/StructTreeRoot 3 0 R/Type/Catalog/ViewerPreferences 13 0 + R/OutputIntents[1 0 R]/Version/1.7>> + endobj + + ... + + 14 0 obj + <>/Rotate 0/StructParents + 0/Tabs/S/Type/Page/Annots[]>> + endobj + + ... + + 31 0 obj + <> + endobj + ``` + + poppler/Catalog.cc | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +commit 384e21b00feab92a176dc022d052f1609004ccc0 +Author: Jan-Michael Brummer +Date: Wed Feb 12 20:55:18 2025 +0100 + + glib: Fix default signature colors + + The newly introduced color conversion function expects 16bit, + not 8bit. + Adjust default colors accordingly. + + Fixes: https://gitlab.freedesktop.org/poppler/poppler/-/issues/1564 + + glib/poppler-form-field.cc | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit 5c50cbabe9c79f7958e1daf8a5e99989f751d7b0 +Author: Albert Astals Cid +Date: Wed Feb 19 17:52:23 2025 +0100 + + Update (C) + + poppler/Page.h | 1 + + 1 file changed, 1 insertion(+) + +commit 01a1f7da81f0eb54a3266c5e69ad97e80fa13bbf +Author: Even Rouault +Date: Thu Feb 6 15:03:27 2025 +0100 + + Add Page::getPageObj() getter + + The GDAL PDF driver + (https://github.com/OSGeo/gdal/tree/master/frmts/pdf) needs low level + access to PDF objects. In particular it needs to access the Object + Page::pageObj private member. To workaround that, GDAL uses nasty + '#define private public' hacks, which beyond being ugly, occasionaly + cause practical build issues such as + https://github.com/OSGeo/gdal/issues/11477 + + Having a clean getter makes things much easier. + + If this change is adopted, the following cleanup will be possible: + https://github.com/rouault/gdal/commit/3594ee6e0ad6f4f806299e3b4c67cb7eb3a1853d + + poppler/Page.h | 3 +++ + 1 file changed, 3 insertions(+) + +commit 4d9e40e643622abc30e75a9efae9492a8727d5d5 +Author: Albert Astals Cid +Date: Wed Feb 5 15:26:52 2025 +0100 + + Update (C) + + cpp/poppler-embedded-file.cpp | 1 + + cpp/poppler-font-private.h | 2 +- + glib/poppler-annot.cc | 1 + + glib/poppler-attachment.cc | 1 + + glib/poppler-media.cc | 1 + + glib/poppler-structure-element.cc | 2 +- + poppler/CIDFontsWidthsBuilder.h | 2 +- + poppler/CMap.cc | 1 + + poppler/CairoFontEngine.cc | 2 +- + poppler/CairoOutputDev.cc | 1 + + poppler/DCTStream.cc | 1 + + poppler/DCTStream.h | 1 + + poppler/Decrypt.cc | 1 + + poppler/Decrypt.h | 1 + + poppler/FlateEncoder.cc | 1 + + poppler/FlateEncoder.h | 1 + + poppler/FlateStream.h | 1 + + poppler/FontInfo.cc | 2 +- + poppler/Function.cc | 1 + + poppler/Gfx.cc | 2 +- + poppler/GfxState.cc | 1 + + poppler/Hints.cc | 1 + + poppler/JBIG2Stream.cc | 1 + + poppler/JBIG2Stream.h | 1 + + poppler/JPEG2000Stream.h | 1 + + poppler/JPXStream.cc | 2 +- + poppler/JPXStream.h | 1 + + poppler/Lexer.cc | 2 +- + poppler/Linearization.cc | 1 + + poppler/OutputDev.cc | 1 + + poppler/PreScanOutputDev.cc | 1 + + poppler/Rendition.cc | 1 + + poppler/Stream.cc | 2 +- + poppler/Stream.h | 2 +- + poppler/TextOutputDev.cc | 4 ++-- + poppler/TextOutputDev.h | 2 +- + poppler/XRef.cc | 2 +- + qt5/src/poppler-embeddedfile.cc | 2 +- + qt5/src/poppler-media.cc | 1 + + qt5/src/poppler-private.h | 2 +- + qt5/src/poppler-sound.cc | 1 + + qt6/src/poppler-embeddedfile.cc | 2 +- + qt6/src/poppler-media.cc | 1 + + qt6/src/poppler-private.h | 2 +- + qt6/src/poppler-sound.cc | 1 + + test/pdf-fullrewrite.cc | 1 + + utils/HtmlOutputDev.cc | 2 +- + utils/ImageOutputDev.cc | 1 + + utils/pdffonts.cc | 2 +- + 49 files changed, 50 insertions(+), 20 deletions(-) + +commit 032d035220702523cf10d924fd436c9e6c83d24c +Author: Sune Vuorela +Date: Wed Feb 5 08:01:50 2025 +0000 + + Enforce error checking on stream::reset + + cpp/poppler-embedded-file.cpp | 4 +- + glib/poppler-attachment.cc | 4 +- + glib/poppler-media.cc | 4 +- + poppler/CMap.cc | 5 +- + poppler/CairoOutputDev.cc | 108 ++++++++++++++++------------- + poppler/DCTStream.cc | 4 +- + poppler/DCTStream.h | 2 +- + poppler/Decrypt.cc | 8 +-- + poppler/Decrypt.h | 6 +- + poppler/FileSpec.cc | 4 +- + poppler/FlateEncoder.cc | 4 +- + poppler/FlateEncoder.h | 2 +- + poppler/FlateStream.h | 2 +- + poppler/Function.cc | 10 ++- + poppler/Gfx.cc | 8 ++- + poppler/GfxFont.cc | 6 +- + poppler/GfxState.cc | 5 +- + poppler/Hints.cc | 31 ++++++--- + poppler/JBIG2Stream.cc | 7 +- + poppler/JBIG2Stream.h | 2 +- + poppler/JPEG2000Stream.h | 2 +- + poppler/JPXStream.cc | 5 +- + poppler/JPXStream.h | 2 +- + poppler/Lexer.cc | 8 ++- + poppler/Linearization.cc | 2 +- + poppler/Object.h | 2 +- + poppler/OutputDev.cc | 8 ++- + poppler/PDFDoc.cc | 51 +++++++++----- + poppler/PSOutputDev.cc | 148 + +++++++++++++++++++++++----------------- + poppler/Page.cc | 22 +++--- + poppler/Parser.cc | 4 +- + poppler/PreScanOutputDev.cc | 8 ++- + poppler/Rendition.cc | 4 +- + poppler/SplashOutputDev.cc | 139 + +++++++++++++++++++------------------ + poppler/Stream.cc | 111 ++++++++++++++---------------- + poppler/Stream.h | 89 ++++++++++++------------ + poppler/XRef.cc | 13 +++- + qt5/src/QPainterOutputDev.cc | 18 +++-- + qt5/src/poppler-document.cc | 3 +- + qt5/src/poppler-embeddedfile.cc | 4 +- + qt5/src/poppler-media.cc | 5 +- + qt5/src/poppler-sound.cc | 4 +- + qt6/src/QPainterOutputDev.cc | 18 +++-- + qt6/src/poppler-document.cc | 3 +- + qt6/src/poppler-embeddedfile.cc | 4 +- + qt6/src/poppler-media.cc | 5 +- + qt6/src/poppler-sound.cc | 4 +- + test/pdf-fullrewrite.cc | 4 +- + utils/HtmlOutputDev.cc | 50 +++++++------- + utils/ImageOutputDev.cc | 43 ++++++++---- + 50 files changed, 575 insertions(+), 434 deletions(-) + +commit 876db3bde971d556ba38ba30dc362b674010cd61 +Author: Stefan Brüns +Date: Sat Dec 14 18:17:48 2024 +0100 + + [TextOutputDev] Replace some gmallocn with vector + + Instead of manually tracking the size of the temporary array just use + a std::vector and push_back. + + Move the various iteration counters and variables from function scope + to the appropriate loop scope. + + poppler/TextOutputDev.cc | 173 + +++++++++++++++++++---------------------------- + 1 file changed, 69 insertions(+), 104 deletions(-) + +commit 7ff20227548af4e3d443b9b33f4c80558adea371 +Author: Stefan Brüns +Date: Fri Dec 13 14:24:48 2024 +0100 + + [TextOutputDev] Use std::sort instead of qsort, minor cleanup/fixes + + Replacing qsort with std::sort is fairly trivial, but gives some extra + type safety and possibly some speedup due to inlining. + + The stricter type checks require to change the argument types of e.g. + TextLineFrag::cmp* methods, but also gets rid of the type cast + (to non-const! TextLineFrag*), likewise for the other classes. + + The return type change may see deliberate, but is necessary as both + +1 and -1 are evaluated as true, and std::sort requires a compare + function which returns bool. Note, the order is indeterminate in case + both elements are compared as same, but this happens with both + std::sort and qsort (man qsort: "If two members compare as equal, + their order in the sorted array is undefined."). + + There are some included changes which are less obvious: + + - std::sort(nullptr, nullptr + 0, ...) is safe (no dereference), + remove + the extra nullptr check (`if (blocks)`). + - No need for the immediate `wordArray` in TextWordList, just sort the + std::vector words. + - TextBlock::cmpYXPrimaryRot is unused, remove it. + + poppler/TextOutputDev.cc | 223 + ++++++++++++++++++----------------------------- + poppler/TextOutputDev.h | 8 +- + 2 files changed, 87 insertions(+), 144 deletions(-) + +commit b9b186de76598da68d40ad2c8806e8ae08db29ae +Author: Stefan Brüns +Date: Fri Mar 29 20:18:45 2024 +0100 + + Simplify last line handling for TextPage::dump + + `!(i < nFrags - 1)` is the same as `i == nFrags -1` (as `i < + nFrags` is + a precondition), i.e. the second check is repetitive - just output + the EOL once for the last line. + + poppler/TextOutputDev.cc | 16 +++++----------- + 1 file changed, 5 insertions(+), 11 deletions(-) + +commit 81448f1c7b3d9dccac5305e18d61b5102657f049 +Author: Albert Astals Cid +Date: Sat Dec 7 12:51:34 2024 +0100 + + CI: Enable bugprone-unchecked-optional-access + + .clang-tidy | 2 +- + cpp/poppler-font-private.h | 10 ++++++---- + glib/poppler-annot.cc | 7 +++++-- + glib/poppler-structure-element.cc | 11 +++++++---- + poppler/CIDFontsWidthsBuilder.h | 4 ++++ + poppler/CairoFontEngine.cc | 3 ++- + poppler/FontInfo.cc | 2 +- + poppler/Gfx.cc | 3 ++- + poppler/PSOutputDev.cc | 20 ++++++++++++-------- + poppler/TextOutputDev.cc | 11 ++++++++++- + qt5/src/poppler-private.h | 15 +++++++++------ + qt6/src/poppler-private.h | 15 +++++++++------ + utils/pdffonts.cc | 16 ++++++++++------ + 13 files changed, 78 insertions(+), 41 deletions(-) + +commit b3e68b8d3c1e92007dfcc129dadadf91fc3ad7bd +Author: Sune Vuorela +Date: Thu Mar 2 09:05:10 2023 +0100 + + Fix compiler warning + + The compiler claims that x2 and y2 might be unitialized. + The compiler might be wrong, but let's just initialize anyways. + + glib/poppler-page.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 19c4009521f0015989691528c71327924d3ca5cf +Author: Albert Astals Cid +Date: Mon Feb 3 23:13:48 2025 +0100 + + Increase version so people that track master can add ifdefs on version + + CMakeLists.txt | 2 +- + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +commit 71fea5bed8a4c047c2063245ba1c5560b071ee25 +Author: Albert Astals Cid +Date: Mon Feb 3 23:00:08 2025 +0100 + + poppler 25.02.0 + + CMakeLists.txt | 4 ++-- + NEWS | 10 ++++++++++ + cpp/Doxyfile | 2 +- + glib/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 6 files changed, 16 insertions(+), 6 deletions(-) + +commit 87dd278d6ea9bae062bafd5a4d456bffba15a2b3 +Author: Albert Astals Cid +Date: Tue Feb 4 00:55:53 2025 +0100 + + Fix build on MSVC + + utils/Win32Console.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 54a0c7c742f3a9bc9ca9409a0c5f61fa46ed5c5a +Author: Albert Astals Cid +Date: Sun Jan 26 10:58:58 2025 +0100 + + Update (C) + + poppler/CairoOutputDev.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 04654ecd3cf903fbb9524d02c443ca5ffb51a1a1 +Author: Nelson Benítez León +Date: Tue Jan 21 00:36:15 2025 +0000 + + cairo: fix issue for images reaching MAX_CAIRO_IMAGE_SIZE + + Fix CairoOutputDev::drawSoftMaskedImage() to not + fail when an image height is >= MAX_CAIRO_IMAGE_SIZE, + instead we clamp the height to what Cairo can handle. + + Fixes #991 + + poppler/CairoOutputDev.cc | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +commit 08091e099ed991d191922a3508cf5faf2e9fe60b +Author: Albert Astals Cid +Date: Mon Jan 20 23:55:48 2025 +0100 + + Update (C) + + poppler/PDFDocEncoding.h | 1 + + utils/pdfinfo.cc | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 7a6973860d07061b0d22ed0d8d3b114430c4555e +Author: Sune Vuorela +Date: Mon Jan 20 09:48:58 2025 +0100 + + cleanup pdfDocEncodingToUtf16 and add some consts + + glib/poppler-form-field.cc | 4 +-- + poppler/Form.cc | 73 + ++++++++++++++++++---------------------------- + poppler/Form.h | 6 ++-- + poppler/PDFDocEncoding.h | 2 +- + qt5/src/poppler-form.cc | 2 +- + qt5/src/poppler-private.cc | 6 ++-- + qt6/src/poppler-form.cc | 2 +- + qt6/src/poppler-private.cc | 6 ++-- + 8 files changed, 40 insertions(+), 61 deletions(-) + +commit 010ca8926562cc49ece8b307a52f5e2bba3cef7e +Author: Sune Vuorela +Date: Mon Jan 20 14:25:59 2025 +0100 + + Clean up utf8ToUcs4 function + + poppler/UTF.cc | 43 +++++++++---------------------------------- + poppler/UTF.h | 6 +----- + utils/pdfinfo.cc | 15 ++++++--------- + 3 files changed, 16 insertions(+), 48 deletions(-) + +commit fa78c71bc2fa823183f7d5b45a5cccab002d3813 +Author: Albert Astals Cid +Date: Mon Jan 20 00:01:03 2025 +0100 + + Update (C) + + poppler/Catalog.h | 2 +- + poppler/CurlPDFDocBuilder.cc | 1 + + poppler/CurlPDFDocBuilder.h | 1 + + poppler/FDPDFDocBuilder.cc | 1 + + poppler/FDPDFDocBuilder.h | 1 + + poppler/FileSpec.h | 2 +- + poppler/Gfx.cc | 1 + + poppler/LocalPDFDocBuilder.cc | 1 + + poppler/LocalPDFDocBuilder.h | 1 + + poppler/OptionalContent.cc | 1 + + poppler/OptionalContent.h | 1 + + poppler/PDFDocBuilder.h | 1 + + poppler/PDFDocFactory.cc | 1 + + poppler/PDFDocFactory.h | 1 + + poppler/Page.cc | 2 +- + poppler/Page.h | 2 +- + qt5/src/poppler-optcontent-private.h | 1 + + qt5/src/poppler-optcontent.cc | 1 + + qt5/src/poppler-optcontent.h | 1 + + qt6/src/poppler-optcontent-private.h | 1 + + qt6/src/poppler-optcontent.cc | 1 + + qt6/src/poppler-optcontent.h | 1 + + 22 files changed, 22 insertions(+), 4 deletions(-) + +commit b20b6ab69b418431b277cb5571010af809298fc4 +Author: Albert Astals Cid +Date: Sun Jan 19 23:48:09 2025 +0100 + + Update (C) + + poppler/GlobalParams.cc | 2 +- + poppler/GlobalParams.h | 2 +- + poppler/GlobalParamsWin.cc | 1 + + poppler/NSSCryptoSignBackend.cc | 2 +- + poppler/UTF.cc | 2 +- + poppler/UTF.h | 2 +- + utils/Win32Console.cc | 1 + + 7 files changed, 7 insertions(+), 5 deletions(-) + +commit b50390f748b59cb0a64b912636ecf6527bd0d12a +Author: Sune Vuorela +Date: Wed Jan 15 16:59:04 2025 +0100 + + Remove gdir, we have std::filesystem + + goo/gdir.h | 96 + ------------------------------------- + goo/gfile.cc | 104 + ---------------------------------------- + poppler/GlobalParams.cc | 74 +++++++++++----------------- + poppler/GlobalParams.h | 15 +++--- + poppler/GlobalParamsWin.cc | 2 +- + poppler/NSSCryptoSignBackend.cc | 11 ++--- + 6 files changed, 41 insertions(+), 261 deletions(-) + +commit 7d9b708cb7dc735e332570a67185a295174a2d78 +Author: Sune Vuorela +Date: Thu Jan 16 11:35:50 2025 +0100 + + Simplify to utf16 conversions + + poppler/PDFDoc.cc | 4 +-- + poppler/UTF.cc | 71 + ++++++++++++-------------------------- + poppler/UTF.h | 15 +++----- + qt5/tests/check_utf_conversion.cpp | 39 +++++---------------- + qt6/tests/check_utf_conversion.cpp | 39 +++++---------------- + utils/Win32Console.cc | 6 ++-- + 6 files changed, 46 insertions(+), 128 deletions(-) + +commit 0d7c1e697358fd736bfb6051afaa9cb20691b8ae +Author: Lucas Baudin +Date: Fri Jan 17 14:33:13 2025 +0100 + + glib: fix a crash when getting a font description for a free text + annotation if it was not set + + The font description of a free text annotation may be null, so this + must be taken care of in the get_font_desc function. + + glib/poppler-annot.cc | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit 74a33aed2b437817b4b1d63ed2bb77bf46e741af +Author: Pablo Correa Gómez +Date: Fri Dec 13 01:17:21 2024 +0100 + + glib: tests: rename macro to more clearly specify its used + + glib/tests/CMakeLists.txt | 50 + +++++++++++++++++++++++------------------------ + 1 file changed, 25 insertions(+), 25 deletions(-) + +commit 6b5084e3e73a2b84459c4e79d99138138fa6df8b +Author: Pablo Correa Gómez +Date: Fri Dec 13 01:03:03 2024 +0100 + + glib: demo: add example on how to disable rendering highlight annots + + glib/demo/render.c | 25 +++++++++++++++++++------ + 1 file changed, 19 insertions(+), 6 deletions(-) + +commit 622c3a7b94bc39a0eb28e9347510f0e0a9d03616 +Author: Pablo Correa Gómez +Date: Fri Dec 13 00:53:38 2024 +0100 + + glib: demo: stop using deprecated functions + + glib/demo/print.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit 0b25f6ccbe426d4c32b643ffa90ad2b950b4ac6e +Author: Pablo Correa Gómez +Date: Thu Oct 10 17:11:46 2024 +0200 + + glib: add new api to allow selecting annotations to render + + This allows consumers to redraw some annotations as desired. In the + process, deprecate the render_printing_with_options, since it becomes + obsolete. + + glib/poppler-page.cc | 87 + ++++++++++++++++++----------------- + glib/poppler-page.h | 6 ++- + glib/poppler.h | 90 + ++++++++++++++++++++++++++++++++++++- + glib/reference/poppler-sections.txt | 1 + + 4 files changed, 141 insertions(+), 43 deletions(-) + +commit 69469de7ad8d72bf2b23d96a95394a45bc8ca2ea +Author: Sune Vuorela +Date: Wed Jan 15 13:28:43 2025 +0100 + + Fewer goostring pointers + + Also more const functions and pointers + + glib/poppler-document.cc | 14 ++++++-------- + poppler/Annot.cc | 2 +- + poppler/Catalog.cc | 6 ++---- + poppler/Catalog.h | 4 ++-- + poppler/FileSpec.cc | 27 +++++++++------------------ + poppler/FileSpec.h | 18 +++++++++--------- + poppler/Gfx.cc | 2 +- + poppler/OptionalContent.cc | 36 + +++++++++++++----------------------- + poppler/OptionalContent.h | 24 +++++++++++------------- + poppler/PDFDoc.h | 2 +- + qt5/src/poppler-optcontent-private.h | 6 +++--- + qt5/src/poppler-optcontent.cc | 8 ++++---- + qt5/src/poppler-optcontent.h | 2 +- + qt5/tests/check_optcontent.cpp | 4 ++-- + qt6/src/poppler-optcontent-private.h | 6 +++--- + qt6/src/poppler-optcontent.cc | 8 ++++---- + qt6/src/poppler-optcontent.h | 2 +- + qt6/tests/check_optcontent.cpp | 4 ++-- + 18 files changed, 75 insertions(+), 100 deletions(-) + +commit ac53e421182487f1b016398556a1d7ddc9b0a77f +Author: Sune Vuorela +Date: Wed Jan 15 14:05:03 2025 +0100 + + More unique pointers and vectors + + PageAttr and PageCache and related helpers + + poppler/Catalog.cc | 22 ++++++---------------- + poppler/Catalog.h | 2 +- + poppler/PDFDoc.cc | 21 +++++---------------- + poppler/PDFDoc.h | 6 +++--- + poppler/Page.cc | 6 ++---- + poppler/Page.h | 6 +++--- + 6 files changed, 20 insertions(+), 43 deletions(-) + +commit 2ed82df7fbd398410a5a42005958aa70699d6b4c +Author: Sune Vuorela +Date: Wed Jan 15 16:57:52 2025 +0100 + + Simplify utf8toutf16 functions + + poppler/UTF.cc | 54 + +++++++++++--------------------------- + poppler/UTF.h | 10 ++----- + qt5/tests/check_utf_conversion.cpp | 15 ++++------- + qt6/tests/check_utf_conversion.cpp | 15 ++++------- + utils/Win32Console.cc | 5 ++-- + 5 files changed, 30 insertions(+), 69 deletions(-) + +commit 5a3689a023718647257b4c32542b06bea4ea6872 +Author: Sune Vuorela +Date: Wed Jan 15 14:20:25 2025 +0100 + + remove guiData + + No one seems to be using it; always hardcoded as nullptr. + + poppler/CurlPDFDocBuilder.cc | 4 ++-- + poppler/CurlPDFDocBuilder.h | 2 +- + poppler/FDPDFDocBuilder.cc | 2 +- + poppler/FDPDFDocBuilder.h | 2 +- + poppler/LocalPDFDocBuilder.cc | 6 +++--- + poppler/LocalPDFDocBuilder.h | 2 +- + poppler/PDFDoc.cc | 7 +++---- + poppler/PDFDoc.h | 11 +++-------- + poppler/PDFDocBuilder.h | 3 +-- + poppler/PDFDocFactory.cc | 4 ++-- + poppler/PDFDocFactory.h | 3 +-- + qt5/src/poppler-private.h | 8 ++++---- + qt6/src/poppler-private.h | 8 ++++---- + 13 files changed, 27 insertions(+), 35 deletions(-) + +commit 50dd001dba665c9fa08316c26e28c2b906eec78a +Author: Ismael Asensio +Date: Mon Jan 13 23:50:46 2025 +0100 + + utils/po: Create temporary .pot.new file in build dir + + This command creates a `.pot.new` temporary file to check for changes + in translation strings, and avoid modifying the source ".pot" file on + trivial date changes. + + Create the temporary file in the build dir, instead of the source dir + + utils/po/CMakeLists.txt | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit eac3e60144d63581fe71be405a4de64af75a8563 +Author: Nelson Benítez León +Date: Sat Apr 6 21:50:24 2024 +0100 + + avoid _render_type3_glyph() stopping subsequent rendering + + When _render_type3_glyph() fails it returns + CAIRO_STATUS_USER_FONT_ERROR + causing the cairo surface to be set in error state + and this is never cleared. + + This prevents further rendering of subsequent content, + while other PDF applications are able to handle this + error condition more gracefully, omitting only the + character or at most the font, with the remainder + of the document showing up fine. + + So let's do the same and change the returned error + into a logging message but return success from + _render_type3_glyph() + + Also included is a fix for a pdftocairo bug: + If a character isn't in /CharProcs at all, then pdftocairo + displays it as the first /CharProcs entry (because + CairoType3Font::create initializes the codeToGID array + to 0). All other programs I tested will display it as a + space or nothing. + + Patch from Jeffrey Morlan + + Fixes issue #1478 + + poppler/CairoFontEngine.cc | 22 +++++++++++++++------- + poppler/CairoFontEngine.h | 2 +- + poppler/CairoOutputDev.cc | 13 +++++++++---- + 3 files changed, 25 insertions(+), 12 deletions(-) + +commit 0ea8b3dc20b84a035e29ced11e2c45e088a7653d +Author: Albert Astals Cid +Date: Wed Jan 8 17:27:57 2025 +0100 + + Update (C) + + cpp/poppler-document.cpp | 1 + + cpp/poppler-private.cpp | 2 +- + cpp/poppler-private.h | 1 + + fofi/FoFiTrueType.cc | 2 +- + fofi/FoFiTrueType.h | 2 +- + fofi/FoFiType1C.cc | 1 + + fofi/FoFiType1C.h | 1 + + glib/poppler-document.cc | 2 +- + glib/poppler-form-field.cc | 2 +- + poppler/Annot.cc | 2 +- + poppler/Annot.h | 2 +- + poppler/CairoFontEngine.cc | 1 + + poppler/Catalog.cc | 2 +- + poppler/DateInfo.cc | 2 +- + poppler/DateInfo.h | 1 + + poppler/FileSpec.cc | 2 +- + poppler/Form.cc | 2 +- + poppler/Form.h | 2 +- + poppler/GfxFont.cc | 2 +- + poppler/GfxFont.h | 2 +- + poppler/Link.cc | 1 + + poppler/Object.h | 2 +- + poppler/Outline.cc | 2 +- + poppler/PDFDoc.cc | 2 +- + poppler/PDFDoc.h | 2 +- + poppler/PSOutputDev.cc | 2 +- + poppler/PSOutputDev.h | 2 +- + poppler/Parser.cc | 2 +- + poppler/SplashOutputDev.cc | 2 +- + qt5/src/QPainterOutputDev.cc | 1 + + qt5/src/QPainterOutputDev.h | 1 + + qt5/src/poppler-annotation.cc | 2 +- + qt5/src/poppler-document.cc | 1 + + qt5/src/poppler-form.cc | 2 +- + qt5/src/poppler-private.cc | 2 +- + qt5/src/poppler-private.h | 2 +- + qt5/tests/check_strings.cpp | 1 + + qt6/src/QPainterOutputDev.cc | 1 + + qt6/src/QPainterOutputDev.h | 1 + + qt6/src/poppler-annotation.cc | 2 +- + qt6/src/poppler-document.cc | 1 + + qt6/src/poppler-form.cc | 2 +- + qt6/src/poppler-private.cc | 2 +- + qt6/src/poppler-private.h | 2 +- + qt6/tests/check_strings.cpp | 1 + + splash/SplashFTFont.cc | 1 + + splash/SplashFTFontEngine.cc | 2 +- + splash/SplashFTFontEngine.h | 2 +- + splash/SplashFTFontFile.cc | 2 +- + splash/SplashFTFontFile.h | 2 +- + splash/SplashFontEngine.cc | 2 +- + splash/SplashFontEngine.h | 2 +- + splash/SplashFontFile.cc | 2 +- + utils/pdfunite.cc | 1 + + 54 files changed, 54 insertions(+), 37 deletions(-) + +commit f79ec6a90e75d28c9cbcfebc93cd05e4b2016802 +Author: Sune Vuorela +Date: Wed Jan 8 15:06:37 2025 +0000 + + use unique_ptr for most owned GooStrings + + cpp/poppler-document.cpp | 104 + ++++++++++++++----------------------- + cpp/poppler-private.cpp | 5 +- + cpp/poppler-private.h | 2 +- + glib/poppler-document.cc | 72 +++++++++++-------------- + glib/poppler-form-field.cc | 12 ++--- + glib/poppler-private.h | 2 +- + poppler/Annot.cc | 12 ++--- + poppler/Annot.h | 4 +- + poppler/Catalog.cc | 4 +- + poppler/DateInfo.cc | 4 +- + poppler/DateInfo.h | 4 +- + poppler/FileSpec.cc | 2 +- + poppler/Form.cc | 43 ++++++++------- + poppler/Form.h | 16 +++--- + poppler/GfxFont.cc | 4 +- + poppler/Link.cc | 2 +- + poppler/Object.h | 6 --- + poppler/Outline.cc | 10 ++-- + poppler/PDFDoc.cc | 15 +++--- + poppler/PDFDoc.h | 19 ++++--- + poppler/Parser.cc | 2 +- + qt5/src/poppler-annotation.cc | 10 ++-- + qt5/src/poppler-document.cc | 4 +- + qt5/src/poppler-form.cc | 20 +++---- + qt5/src/poppler-private.cc | 23 ++++---- + qt5/src/poppler-private.h | 4 +- + qt5/tests/check_strings.cpp | 4 +- + qt5/tests/check_utf_conversion.cpp | 4 +- + qt6/src/poppler-annotation.cc | 10 ++-- + qt6/src/poppler-document.cc | 4 +- + qt6/src/poppler-form.cc | 20 +++---- + qt6/src/poppler-private.cc | 23 ++++---- + qt6/src/poppler-private.h | 4 +- + qt6/tests/check_strings.cpp | 4 +- + qt6/tests/check_utf_conversion.cpp | 4 +- + utils/pdfunite.cc | 8 +-- + 36 files changed, 206 insertions(+), 284 deletions(-) + +commit 8c0980ed360d55cd5b86ba7de8735517e5a0532a +Author: Albert Astals Cid +Date: Wed Jan 8 12:59:12 2025 +0100 + + Make it compile with clang 15 + + poppler/PSOutputDev.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit 2a123366ade2be602485ddd66313a1919cc23bcc +Author: Albert Astals Cid +Date: Wed Jan 8 12:45:31 2025 +0100 + + CI: Also compile with clang 15 in Ubuntu + + .gitlab-ci.yml | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +commit 084a250912e7b3da2946c1f6a27ba9e3c31a88e5 +Author: Albert Astals Cid +Date: Wed Jan 8 12:08:28 2025 +0100 + + CI: Remove unneeded path + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit e3773a71fc7dd6d0e8428989ab7fb8578e53e64b +Author: Sune Vuorela +Date: Wed Jan 8 08:36:14 2025 +0000 + + Fonts: cidMap is a vector + + fofi/FoFiTrueType.cc | 48 +++++++++-------- + fofi/FoFiTrueType.h | 18 +++---- + fofi/FoFiType1C.cc | 119 + +++++++++++++++++++------------------------ + fofi/FoFiType1C.h | 7 +-- + poppler/CairoFontEngine.cc | 46 ++++++----------- + poppler/GfxFont.cc | 42 ++++----------- + poppler/GfxFont.h | 13 +++-- + poppler/PSOutputDev.cc | 86 +++++++++---------------------- + poppler/PSOutputDev.h | 4 +- + poppler/SplashOutputDev.cc | 36 ++++--------- + qt5/src/QPainterOutputDev.cc | 58 ++++++++------------- + qt5/src/QPainterOutputDev.h | 6 +-- + qt6/src/QPainterOutputDev.cc | 62 +++++++++------------- + qt6/src/QPainterOutputDev.h | 6 +-- + splash/SplashFTFont.cc | 6 +-- + splash/SplashFTFontEngine.cc | 10 ++-- + splash/SplashFTFontEngine.h | 5 +- + splash/SplashFTFontFile.cc | 24 ++++----- + splash/SplashFTFontFile.h | 11 ++-- + splash/SplashFontEngine.cc | 12 ++--- + splash/SplashFontEngine.h | 5 +- + splash/SplashFontFile.cc | 2 - + 22 files changed, 243 insertions(+), 383 deletions(-) + +commit d54ada8a69284f6562f84fc9e37facf74d7387b1 +Author: Albert Astals Cid +Date: Sun Jan 5 22:46:30 2025 +0100 + + Update (C) + + poppler/Annot.cc | 2 +- + poppler/DCTStream.cc | 1 + + poppler/DCTStream.h | 1 + + poppler/Decrypt.cc | 1 + + poppler/Decrypt.h | 1 + + poppler/FlateEncoder.cc | 1 + + poppler/FlateEncoder.h | 1 + + poppler/FlateStream.cc | 1 + + poppler/FlateStream.h | 1 + + poppler/JBIG2Stream.cc | 2 +- + poppler/JBIG2Stream.h | 1 + + poppler/JPEG2000Stream.cc | 2 +- + poppler/JPEG2000Stream.h | 2 +- + poppler/JPXStream.cc | 2 +- + poppler/JPXStream.h | 2 +- + poppler/PSOutputDev.cc | 2 +- + poppler/Stream.cc | 1 + + poppler/Stream.h | 2 +- + 18 files changed, 18 insertions(+), 8 deletions(-) + +commit 5d71c95d08aa0e097f711254e1c69ee0cf275328 +Author: Nelson Benítez León +Date: Sun Jan 5 21:33:23 2025 +0000 + + Detect malformed Streams in Annot Appearances + + poppler/Annot.cc | 5 +++ + poppler/DCTStream.cc | 6 ++- + poppler/DCTStream.h | 2 +- + poppler/Decrypt.cc | 12 ++++-- + poppler/Decrypt.h | 6 +-- + poppler/FlateEncoder.cc | 5 ++- + poppler/FlateEncoder.h | 2 +- + poppler/FlateStream.cc | 4 +- + poppler/FlateStream.h | 2 +- + poppler/JBIG2Stream.cc | 4 +- + poppler/JBIG2Stream.h | 2 +- + poppler/JPEG2000Stream.cc | 4 +- + poppler/JPEG2000Stream.h | 2 +- + poppler/JPXStream.cc | 6 ++- + poppler/JPXStream.h | 2 +- + poppler/PSOutputDev.cc | 6 +-- + poppler/Stream.cc | 98 + +++++++++++++++++++++++++++++++++-------------- + poppler/Stream.h | 54 ++++++++++++++------------ + 18 files changed, 145 insertions(+), 77 deletions(-) + +commit 3ead1238555ce14dd0c619f1097d552307e6e40a +Author: Albert Astals Cid +Date: Thu Jan 2 00:34:25 2025 +0100 + + poppler 25.01.0 + + CMakeLists.txt | 6 +++--- + NEWS | 25 +++++++++++++++++++++++++ + cpp/CMakeLists.txt | 2 +- + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 6 files changed, 32 insertions(+), 7 deletions(-) + +commit 3a09377b42f69db66aa9233fb5f5584579508c8d +Author: Albert Astals Cid +Date: Thu Jan 2 00:13:25 2025 +0100 + + Welcome 2025 + + poppler/poppler-config.h.cmake | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2a8f036927abec319fa400a76b46c87bfb0b56ac +Author: Albert Astals Cid +Date: Thu Jan 2 00:13:20 2025 +0100 + + Update (C) + + poppler/Form.cc | 2 +- + poppler/PSOutputDev.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 25162097d7f182388da27d6ec2b27da61e1ff17b +Author: Albert Astals Cid +Date: Thu Jan 2 01:01:04 2025 +0100 + + Make it compile with clang 15 + + poppler/PSOutputDev.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit 221914fb062253b879c9fbf5a58e89330e679313 +Author: Albert Astals Cid +Date: Thu Jan 2 00:10:50 2025 +0100 + + Add missing std::move + + poppler/Form.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ccdd172fc6361b45674639bfdc44f209038af77d +Author: Pablo Correa Gómez +Date: Sun Dec 29 02:56:51 2024 +0100 + + glib: rename border_width to just width for annots + + This also fixes the introspection data, since the header file is using + only width too + + glib/poppler-annot.cc | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +commit 2391c8d0685c0f29019084d674ad4ea17b9939a1 +Author: Pablo Correa Gómez +Date: Sun Dec 29 02:54:50 2024 +0100 + + glib: mark transfer of poppler_page_get_selected_region + + And use the more modern syntax + + glib/poppler-page.cc | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +commit 26daa2a03963c1ceb5a61d6001815ef0f4b8d21d +Author: Albert Astals Cid +Date: Tue Dec 24 20:21:54 2024 +0100 + + Splash::gouraudTriangleShadedFill: Check for overflow in some + operations + + Fixes #1554 + + splash/Splash.cc | 32 ++++++++++++++++++++++++++++++-- + 1 file changed, 30 insertions(+), 2 deletions(-) + +commit ce8e62996102f43b2b7f6fba4c2f2d644b81f68a +Author: Albert Astals Cid +Date: Sat Dec 21 12:13:04 2024 +0100 + + Update (C) + + poppler/TextOutputDev.h | 1 + + qt5/src/poppler-page.cc | 1 + + qt6/src/poppler-page.cc | 1 + + test/perf-test.cc | 1 + + 4 files changed, 4 insertions(+) + +commit d2609165be8815cfa613f71df7369c59a41f7a18 +Author: Sune Vuorela +Date: Thu Dec 19 13:34:01 2024 +0100 + + TextOutputDev: memory cleanups + + Return some GooStrings by value, and convert a std::vector** to + a vector + of vectors + + glib/poppler-page.cc | 73 ++++++++++++-------------------- + poppler/TextOutputDev.cc | 108 + ++++++++++++++++------------------------------- + poppler/TextOutputDev.h | 10 ++--- + qt5/src/poppler-page.cc | 5 +-- + qt6/src/poppler-page.cc | 5 +-- + test/perf-test.cc | 7 +-- + 6 files changed, 74 insertions(+), 134 deletions(-) + +commit a3fd179de9002c26da5cac62ca728894808e6513 +Author: Albert Astals Cid +Date: Fri Dec 20 12:32:40 2024 +0100 + + Update (C) + + poppler/NameToCharCode.cc | 1 + + poppler/NameToCharCode.h | 1 + + 2 files changed, 2 insertions(+) + +commit 99c9fe76ed537939be665c183cfd31a3e7925a74 +Author: Sune Vuorela +Date: Thu Dec 19 13:26:18 2024 +0100 + + NameToCharCode: remove custom hashmap + + Using std::unordered_map makes the code quite a bit simpler + + poppler/NameToCharCode.cc | 100 + ++-------------------------------------------- + poppler/NameToCharCode.h | 22 +++++----- + 2 files changed, 15 insertions(+), 107 deletions(-) + +commit 1509b915ef1c826d40b6dccabb7f1fabf62e5f84 +Author: Sune Vuorela +Date: Thu Dec 19 13:13:55 2024 +0100 + + Form: remove grealloc in favor of a vector + + poppler/Form.cc | 13 ++----------- + poppler/Form.h | 8 +++----- + 2 files changed, 5 insertions(+), 16 deletions(-) + +commit 382908d6431dbd2f8cfa612fed998a0872a3ced8 +Author: Albert Astals Cid +Date: Fri Dec 20 10:42:35 2024 +0100 + + Update (C) + + qt5/src/poppler-document.cc | 1 + + qt5/src/poppler-form.cc | 1 + + qt6/src/poppler-document.cc | 1 + + qt6/src/poppler-form.cc | 1 + + 4 files changed, 4 insertions(+) + +commit 8b078fbf696d8838cf89ccabc67f2fdbca86af5c +Author: Stefan Brüns +Date: Mon Dec 16 20:45:17 2024 +0100 + + qt: Fix QDateTime TimeSpec deprecations + + qt5/src/poppler-document.cc | 9 +++++---- + qt5/src/poppler-form.cc | 5 +++-- + qt6/src/poppler-document.cc | 9 +++++---- + qt6/src/poppler-form.cc | 5 +++-- + 4 files changed, 16 insertions(+), 12 deletions(-) + +commit afcfac2fd0838448b3fa0bf90c1c22c142fa90e1 +Author: Stefan Brüns +Date: Wed Apr 10 20:38:34 2024 +0200 + + qt: Use std::string instead of plain C string for QString + initialization + + Both are mostly equivalent, but using the std::string avoids the + implicit strlen call. + + qt5/src/poppler-page.cc | 2 +- + qt6/src/poppler-page.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit ab35ab3c8ba75f776f81a530cae036c4c662312f +Author: Albert Astals Cid +Date: Wed Dec 18 23:29:39 2024 +0100 + + Update (C) + + poppler/JBIG2Stream.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ade9b5ebed44b0c15522c27669ef6cdf93eff84e +Author: Albert Astals Cid +Date: Tue Dec 17 18:59:01 2024 +0100 + + JBIG2Bitmap::combine: Fix crash on malformed files + + Fixes #1553 + + poppler/JBIG2Stream.cc | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +commit db6ca34131c41a91c7f3654e0e03f4ca7043723f +Author: Albert Astals Cid +Date: Mon Dec 16 15:04:35 2024 +0100 + + Update (C) + + cpp/poppler-image.cpp | 2 +- + cpp/poppler-image.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 18885fcc7398ddaa0769a2ae7acad46d32db02f9 +Author: Albert Astals Cid +Date: Mon Dec 16 14:44:24 2024 +0100 + + Remove QT_VERSION_CHECK that are always true + + We require Qt 5.15 now + + qt5/src/poppler-annotation.cc | 10 ---------- + 1 file changed, 10 deletions(-) + +commit e21be2a06e6a8664735b726e4c605c64ed6d5141 +Author: Albert Astals Cid +Date: Mon Dec 16 14:43:11 2024 +0100 + + Remove #if GTK_CHECK_VERSION that are now always true + + Since we require gtk 3.24 + + glib/demo/annots.c | 9 --------- + glib/demo/find.c | 4 ---- + glib/demo/forms.c | 5 ----- + glib/demo/images.c | 4 ---- + glib/demo/info.cc | 5 ----- + glib/demo/layers.c | 4 ---- + glib/demo/main.c | 8 -------- + glib/demo/page.c | 10 ---------- + glib/demo/render.c | 4 ---- + glib/demo/selections.c | 4 ---- + glib/demo/signature.c | 4 ---- + glib/demo/text.c | 5 ----- + glib/demo/utils.c | 23 ----------------------- + test/gtk-test.cc | 4 ---- + 14 files changed, 93 deletions(-) + +commit 5055c9343f1b00f6415dc16843f41b2d4eaa991e +Author: Albert Astals Cid +Date: Mon Dec 16 14:36:58 2024 +0100 + + Remove #if GLIB_CHECK_VERSION(2, 68, 0) since we require 2.72 + + glib/poppler-document.cc | 4 ---- + 1 file changed, 4 deletions(-) + +commit 715f7b7ce4f58085e2d7a84f788ed586f95c51f1 +Author: Albert Astals Cid +Date: Mon Dec 16 12:52:55 2024 +0100 + + Remove runtime check for lcms > 2.10 + + We depend on 2.12 now + + poppler/GfxState.cc | 13 +------------ + 1 file changed, 1 insertion(+), 12 deletions(-) + +commit 40dbe290705e622dd1dc649ff717a572e8d903f9 +Author: Albert Astals Cid +Date: Mon Dec 16 12:45:14 2024 +0100 + + Remove if LCMS_VERSION >= 2070 + + We require 2.12 now + + poppler/GfxState.cc | 4 ---- + 1 file changed, 4 deletions(-) + +commit a62417aae8276827da0bae2be471e71313140d4d +Author: Albert Astals Cid +Date: Mon Dec 16 12:44:16 2024 +0100 + + Remove "REMOVE-ME-IN-THE-FUTURE" + + The future is now, that fix is part of lcms 2.12 which we now require + + poppler/GfxState.cc | 11 ----------- + 1 file changed, 11 deletions(-) + +commit dacedc7c78d62b2d7fa7b391ab79a21467da0f25 +Author: Albert Astals Cid +Date: Mon Dec 16 12:37:18 2024 +0100 + + Somehow missed increasing these deps when updating to Ubuntu 22.04 + as min base + + CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 82cc4a30050ad39debce85a2b5f6e449cd7046f5 +Author: Albert Astals Cid +Date: Mon Dec 16 12:09:55 2024 +0100 + + cpp: Remove rect parameter from image::copy, it was never implemented + + cpp/poppler-image.cpp | 20 ++++++-------------- + cpp/poppler-image.h | 2 +- + 2 files changed, 7 insertions(+), 15 deletions(-) + +commit d0003fe2b5674fc3e2fadead5771df290a2ea974 +Author: Sune Vuorela +Date: Sun Dec 15 10:38:09 2024 +0000 + + Memory cleanup in form. + + poppler/Form.cc | 189 + +++++++++++++++++++++----------------------------------- + poppler/Form.h | 31 ++++------ + poppler/Page.cc | 12 +--- + poppler/Page.h | 2 +- + 4 files changed, 89 insertions(+), 145 deletions(-) + +commit 7450bbda17c583f2602ccdb6be5c6b02bf87b6f7 +Author: Albert Astals Cid +Date: Sat Dec 14 15:05:40 2024 +0100 + + Enable clang-tidy modernize-use-equals-default + + .clang-tidy | 2 +- + cpp/poppler-font-private.h | 2 +- + cpp/poppler-global.cpp | 8 ++--- + cpp/poppler-global.h | 2 +- + cpp/poppler-image.cpp | 2 +- + cpp/poppler-rectangle.cpp | 2 +- + cpp/poppler-rectangle.h | 2 +- + cpp/poppler-toc.cpp | 4 +-- + fofi/FoFiIdentifier.cc | 6 ++-- + goo/ImgWriter.cc | 2 +- + poppler/Annot.cc | 6 ++-- + poppler/AnnotStampImageHelper.h | 2 +- + poppler/Array.cc | 2 +- + poppler/CMap.cc | 2 +- + poppler/CachedFile.cc | 2 -- + poppler/CachedFile.h | 2 +- + poppler/CairoFontEngine.cc | 6 ++-- + poppler/CairoRescaleBox.h | 4 +-- + poppler/Catalog.cc | 2 +- + poppler/Decrypt.cc | 4 +-- + poppler/FontInfo.cc | 2 +- + poppler/Form.cc | 2 +- + poppler/Form.h | 2 +- + poppler/Function.cc | 6 ++-- + poppler/GfxState.cc | 36 +++++++++---------- + poppler/GfxState.h | 2 +- + poppler/GlobalParams.cc | 2 +- + poppler/JBIG2Stream.cc | 8 ++--- + poppler/JSInfo.cc | 2 -- + poppler/JSInfo.h | 2 +- + poppler/Linearization.cc | 2 +- + poppler/Movie.cc | 2 -- + poppler/Movie.h | 2 +- + poppler/Object.h | 2 +- + poppler/PDFDoc.cc | 2 +- + poppler/PSTokenizer.cc | 2 -- + poppler/PSTokenizer.h | 2 +- + poppler/Page.cc | 2 +- + poppler/PageTransition.cc | 2 -- + poppler/PageTransition.h | 2 +- + poppler/PreScanOutputDev.cc | 2 +- + poppler/Rendition.cc | 4 --- + poppler/Rendition.h | 4 +-- + poppler/SecurityHandler.cc | 2 +- + poppler/Sound.cc | 2 +- + poppler/SplashOutputDev.cc | 10 +++--- + poppler/Stream.cc | 12 +++---- + poppler/StructElement.cc | 2 +- + poppler/StructTreeRoot.h | 9 ++--- + poppler/TextOutputDev.cc | 8 ++--- + poppler/UnicodeMap.cc | 2 +- + poppler/ViewerPreferences.cc | 2 +- + qt5/demos/abstractinfodock.cpp | 2 +- + qt5/demos/documentobserver.cpp | 2 +- + qt5/demos/embeddedfiles.cpp | 2 +- + qt5/demos/fonts.cpp | 2 +- + qt5/demos/info.cpp | 2 +- + qt5/demos/metadata.cpp | 2 +- + qt5/demos/navigationtoolbar.cpp | 2 +- + qt5/demos/optcontent.cpp | 2 +- + qt5/demos/pageview.cpp | 2 +- + qt5/demos/permissions.cpp | 2 +- + qt5/demos/thumbnails.cpp | 2 +- + qt5/demos/toc.cpp | 2 +- + qt5/src/poppler-annotation.cc | 62 + ++++++++++++++++---------------- + qt5/src/poppler-base-converter.cc | 2 +- + qt5/src/poppler-form.cc | 14 ++++---- + qt5/src/poppler-link.cc | 28 +++++++-------- + qt5/src/poppler-optcontent.cc | 4 +-- + qt5/src/poppler-pdf-converter.cc | 2 +- + qt5/src/poppler-private.h | 2 +- + qt5/src/poppler-ps-converter.cc | 2 +- + qt5/src/poppler-qiodeviceoutstream.cc | 2 +- + qt6/demos/abstractinfodock.cpp | 2 +- + qt6/demos/documentobserver.cpp | 2 +- + qt6/demos/embeddedfiles.cpp | 2 +- + qt6/demos/fonts.cpp | 2 +- + qt6/demos/info.cpp | 2 +- + qt6/demos/metadata.cpp | 2 +- + qt6/demos/navigationtoolbar.cpp | 2 +- + qt6/demos/optcontent.cpp | 2 +- + qt6/demos/pageview.cpp | 2 +- + qt6/demos/permissions.cpp | 2 +- + qt6/demos/thumbnails.cpp | 2 +- + qt6/demos/toc.cpp | 2 +- + qt6/src/poppler-annotation.cc | 68 + +++++++++++++++++------------------ + qt6/src/poppler-base-converter.cc | 2 +- + qt6/src/poppler-form.cc | 14 ++++---- + qt6/src/poppler-link-extractor.cc | 2 +- + qt6/src/poppler-link.cc | 28 +++++++-------- + qt6/src/poppler-optcontent.cc | 4 +-- + qt6/src/poppler-pdf-converter.cc | 4 +-- + qt6/src/poppler-private.h | 2 +- + qt6/src/poppler-ps-converter.cc | 2 +- + qt6/src/poppler-qiodeviceoutstream.cc | 2 +- + qt6/tests/test-password-qt6.cpp | 2 +- + qt6/tests/test-poppler-qt6.cpp | 2 +- + splash/SplashFTFont.cc | 2 +- + splash/SplashFontFileID.cc | 4 +-- + splash/SplashPattern.cc | 6 ++-- + splash/SplashXPathScanner.cc | 2 +- + test/gtk-test.cc | 2 +- + utils/HtmlFonts.cc | 4 +-- + utils/HtmlFonts.h | 18 +--------- + utils/HtmlLinks.cc | 4 +-- + 105 files changed, 253 insertions(+), 294 deletions(-) + +commit fc8c9d6519ee882f7ea7860ca7d04a5d07fc2f4f +Author: Albert Astals Cid +Date: Sat Dec 14 10:30:06 2024 +0100 + + Update (C) + + poppler/UnicodeMap.cc | 1 + + poppler/UnicodeMap.h | 1 + + 2 files changed, 2 insertions(+) + +commit 29d5c8885b67608fd34b35b4e972cc61d9042e02 +Author: Sune Vuorela +Date: Thu Nov 21 13:43:11 2024 +0100 + + Simplify memory management in UnicodeMap + + Also prevent a couple of out of bounds writes with unexpected sizes. + + poppler/GlobalParams.cc | 8 +- + poppler/UnicodeMap.cc | 228 + ++++++++++++++++-------------------------------- + poppler/UnicodeMap.h | 27 +++--- + 3 files changed, 89 insertions(+), 174 deletions(-) + +commit 2925f96694e86a4c6ace1925ab5d5cf5a239db9d +Author: Sune Vuorela +Date: Thu Dec 12 15:54:37 2024 +0100 + + Add unit test for another case in cidfontswidthsbuilder + + qt6/tests/check_cidfontswidthsbuilder.cpp | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +commit 06b598b675820fd3bf5f94060a6cbefc74d0dde9 +Author: Stefan Brüns +Date: Sun Dec 8 12:20:21 2024 +0100 + + Replace word insertion sort with merge sort + + The block and page pools only need to be sorted before coalescing + them. + As insertion sort for linked lists is O(n^2), the runtime may become + unbearably slow, while a merge sort at the end will run in O(n log n). + + poppler/TextOutputDev.cc | 175 + +++++++++++++++++++++++++++++------------------ + poppler/TextOutputDev.h | 15 ++-- + 2 files changed, 119 insertions(+), 71 deletions(-) + +commit 04d64a92402fe3aadad5ea4decdfb5dba666cf6f +Author: Albert Astals Cid +Date: Sun Dec 8 16:04:18 2024 +0100 + + Enable modernize-use-ranges + + .clang-tidy | 2 +- + cpp/poppler-document.cpp | 2 +- + cpp/poppler-image.cpp | 2 +- + poppler/CIDFontsWidthsBuilder.h | 5 +++-- + poppler/Catalog.cc | 9 ++++++++- + poppler/Dict.cc | 5 +++-- + poppler/GlobalParams.cc | 2 +- + poppler/TextOutputDev.cc | 2 +- + qt5/src/poppler-optcontent.cc | 4 ++-- + qt6/src/poppler-optcontent.cc | 4 ++-- + splash/SplashFontEngine.cc | 4 ++-- + 11 files changed, 25 insertions(+), 16 deletions(-) + +commit e3a2baa1373a56cb27481434c9eaa347498a5ed0 +Author: Albert Astals Cid +Date: Sat Dec 7 12:51:34 2024 +0100 + + CI: Enable bugprone-inc-dec-in-conditions + + .clang-tidy | 2 +- + poppler/CachedFile.cc | 8 +++++--- + 2 files changed, 6 insertions(+), 4 deletions(-) + +commit 99f76496d6063accff5aa7cf4ce2524ac651a6a2 +Author: Albert Astals Cid +Date: Sat Nov 2 23:11:20 2024 +0100 + + CI: Fedora 41 for clazy + + .gitlab-ci.yml | 6 +++--- + qt5/src/poppler-link.cc | 18 +++++++++--------- + qt5/tests/check_forms.cpp | 2 +- + qt5/tests/stress-poppler-dir.cpp | 2 +- + qt5/tests/stress-poppler-qt5.cpp | 2 +- + qt6/src/poppler-link.cc | 18 +++++++++--------- + qt6/tests/check_forms.cpp | 2 +- + qt6/tests/stress-poppler-dir.cpp | 2 +- + qt6/tests/stress-poppler-qt6.cpp | 2 +- + 9 files changed, 27 insertions(+), 27 deletions(-) + +commit 181653d19b7d7e63d764c32e922b3c5bf90a2265 +Author: Albert Astals Cid +Date: Mon Dec 9 21:53:50 2024 +0100 + + qt: Fix crash in Submit Forms if document links to non existing field + + qt5/src/poppler-page.cc | 2 +- + qt6/src/poppler-page.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 45a3eb915a216044657a7ace7dee189dbfc5f6cf +Author: Stefan Brüns +Date: Thu Apr 4 22:39:11 2024 +0200 + + [TextOutputDev] Better detect fakebold words + + Currently, the fakebold detection code only handles multiple + instances of + identical words, but fails in several cases: + + - the last instance includes e.g. a trailing colon or dot + - the bold word is "quoted", i.e. the first instance includes + the opening + quote, the last instance the closing quote. + - a non-bold and a bold word are joined with a hyphen + + To handle these cases, the matching logic is extended to detect more + of these cases. + + To avoid a performance penalty the first check is a bounding box + overlap test, as it is fast and a requirement for every fakebold case. + + For the remaining pairs, a number of possible matches is evaluated: + 1. same words, as previously + 2. check if one word is the prefix of the other + 3. check if the tail of one word is the prefix of the other + + Identical words are discarded as before, while for partial matches + the overlapping part is discarded from the second word. + + poppler/TextOutputDev.cc | 99 + +++++++++++++++++++++++++++++++++++------- + qt5/tests/check_actualtext.cpp | 12 ----- + qt6/tests/check_actualtext.cpp | 12 ----- + 3 files changed, 83 insertions(+), 40 deletions(-) + +commit dfd458aafd558d4be054b7e872da5d3ba0e9ae0a +Author: Albert Astals Cid +Date: Sun Dec 8 01:07:41 2024 +0100 + + Enable modernize-raw-string-literal + + .clang-tidy | 2 +- + qt5/tests/check_distinguished_name_parser.cpp | 12 ++++++------ + qt6/tests/check_distinguished_name_parser.cpp | 12 ++++++------ + utils/HtmlOutputDev.cc | 22 +++++++++++----------- + utils/pdftotext.cc | 16 ++++++++-------- + 5 files changed, 32 insertions(+), 32 deletions(-) + +commit 7984e15ef2005fa2d94f00896e661aa629704ca3 +Author: Albert Astals Cid +Date: Sun Dec 8 01:05:01 2024 +0100 + + Enable modernize-use-nullptr + + .clang-tidy | 2 +- + glib/poppler-form-field.cc | 14 +++++++------- + 2 files changed, 8 insertions(+), 8 deletions(-) + +commit cdd96bc290b17189ca3b0145e72f37292ad22daf +Author: Albert Astals Cid +Date: Sun Dec 8 01:02:34 2024 +0100 + + Enable modernize-use-std-numbers + + .clang-tidy | 2 +- + poppler/Annot.cc | 5 +++-- + utils/pdfinfo.cc | 9 +++++---- + 3 files changed, 9 insertions(+), 7 deletions(-) + +commit 8c57732ebd6cb13ca93eaf2a0b9861c6b18f4b77 +Author: Albert Astals Cid +Date: Sun Dec 8 00:28:04 2024 +0100 + + CI: Enable clang-tidy modernize checks + + And disable the ones that fail + + .clang-tidy | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ac055a9a42cfbd99727b749ff758d549358e04f0 +Author: Albert Astals Cid +Date: Sat Dec 7 12:51:34 2024 +0100 + + CI: Enable bugprone-multi-level-implicit-pointer-conversion + + .clang-tidy | 2 +- + fofi/FoFiType1.cc | 2 +- + fofi/FoFiType1C.cc | 2 +- + glib/poppler-page.cc | 4 ++-- + poppler/CairoOutputDev.cc | 4 ++-- + poppler/CharCodeToUnicode.cc | 6 +++--- + poppler/Form.cc | 22 +++++++++++----------- + poppler/Function.cc | 2 +- + poppler/GfxState.cc | 10 +++++----- + poppler/Hints.cc | 2 +- + poppler/JBIG2Stream.cc | 16 ++++++++-------- + poppler/NSSCryptoSignBackend.cc | 6 +++--- + poppler/PDFDoc.cc | 2 +- + poppler/TextOutputDev.cc | 26 +++++++++++++------------- + test/gtk-test.cc | 2 +- + 15 files changed, 54 insertions(+), 54 deletions(-) + +commit ab975e161fb86542ef46bf95f5dbead01896953b +Author: Albert Astals Cid +Date: Sat Dec 7 22:47:39 2024 +0100 + + CI: Fix readability-redundant-string-init + + glib/poppler-annot.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 3dc162aca81f598ee8ca67a50204c3e30e43be6b +Author: Albert Astals Cid +Date: Sat Dec 7 22:46:51 2024 +0100 + + CI: Fix readability-container-contains + + poppler/CairoOutputDev.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit 0bf9b46b72f572927e753b4a72c9dfbdc28a43ad +Author: Albert Astals Cid +Date: Sat Dec 7 22:43:49 2024 +0100 + + CI: Fix readability-static-accessed-through-instance + + cpp/poppler-page-renderer.cpp | 2 +- + qt5/demos/main_viewer.cpp | 2 +- + qt5/tests/test-password-qt5.cpp | 2 +- + qt5/tests/test-poppler-qt5.cpp | 4 ++-- + qt6/demos/main_viewer.cpp | 2 +- + qt6/tests/test-password-qt6.cpp | 2 +- + qt6/tests/test-poppler-qt6.cpp | 4 ++-- + 7 files changed, 9 insertions(+), 9 deletions(-) + +commit 752f6afaad63d80040d1895522e2009e3e53e561 +Author: Albert Astals Cid +Date: Sat Dec 7 22:26:36 2024 +0100 + + Fix readability-uniqueptr-delete-release + + glib/poppler-document.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 5e2399e2afc0b613dc77af4f6c453b6deb4c550e +Author: Albert Astals Cid +Date: Sat Dec 7 17:09:50 2024 +0100 + + CI: Ignore readability-identifier-length + + .clang-tidy | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 946ac18a2947d087a7d3c48b34e7798655bfa49a +Author: Albert Astals Cid +Date: Sat Dec 7 16:45:05 2024 +0100 + + CI: Enable readability-redundant-access-specifiers + + .clang-tidy | 2 +- + poppler/PageLabelInfo.h | 1 - + poppler/Stream.h | 1 - + qt5/demos/abstractinfodock.h | 3 +-- + qt5/demos/navigationtoolbar.cpp | 8 ++++---- + qt5/demos/navigationtoolbar.h | 3 +-- + qt5/demos/optcontent.h | 3 +-- + qt5/demos/pageview.h | 1 - + qt5/demos/thumbnails.h | 3 +-- + qt5/demos/viewer.h | 3 +-- + qt5/src/poppler-form.h | 2 +- + qt5/tests/check_actualtext.cpp | 6 +++--- + qt5/tests/check_signature_basics.cpp | 2 +- + qt5/tests/check_strings.cpp | 11 +++++------ + qt6/demos/abstractinfodock.h | 3 +-- + qt6/demos/navigationtoolbar.cpp | 8 ++++---- + qt6/demos/navigationtoolbar.h | 3 +-- + qt6/demos/optcontent.h | 3 +-- + qt6/demos/pageview.h | 1 - + qt6/demos/thumbnails.h | 3 +-- + qt6/demos/viewer.h | 3 +-- + qt6/src/poppler-form.h | 2 +- + qt6/tests/check_actualtext.cpp | 6 +++--- + qt6/tests/check_signature_basics.cpp | 2 +- + qt6/tests/check_strings.cpp | 11 +++++------ + utils/InMemoryFile.h | 1 - + 26 files changed, 39 insertions(+), 56 deletions(-) + +commit 569fae7923d401cd59f4a36a5bc2a37623b283f8 +Author: Albert Astals Cid +Date: Sat Dec 7 16:26:24 2024 +0100 + + CI: Enable readability-delete-null-pointer + + .clang-tidy | 2 +- + fofi/FoFiType1C.cc | 4 +--- + poppler/CairoOutputDev.cc | 12 +++--------- + poppler/GfxFont.cc | 12 +++--------- + poppler/GfxState.cc | 5 +---- + poppler/JBIG2Stream.cc | 8 ++------ + poppler/PSOutputDev.cc | 24 ++++++------------------ + poppler/Page.cc | 2 +- + poppler/SplashOutputDev.cc | 20 +++++--------------- + poppler/Stream.cc | 8 ++------ + poppler/TextOutputDev.cc | 14 ++++---------- + splash/SplashFontEngine.cc | 4 +--- + utils/HtmlOutputDev.cc | 4 +--- + utils/pdftocairo.cc | 16 ++++------------ + utils/pdftohtml.cc | 4 +--- + 15 files changed, 36 insertions(+), 103 deletions(-) + +commit 6b38ad4acb5019240fb823d15da2ca5e9e198c97 +Author: Albert Astals Cid +Date: Sat Dec 7 16:23:56 2024 +0100 + + CI: Enable readability-redundant-control-flow + + .clang-tidy | 2 +- + cpp/poppler-page.cpp | 1 - + poppler/Function.cc | 2 -- + poppler/Gfx.cc | 2 -- + poppler/GfxState.cc | 5 +---- + poppler/JBIG2Stream.cc | 1 - + poppler/OutputDev.cc | 5 +---- + poppler/PDFDoc.cc | 1 - + poppler/SplashOutputDev.cc | 1 - + test/perf-test.cc | 1 - + 10 files changed, 3 insertions(+), 18 deletions(-) + +commit dc6de6a26f2b599b8fa97a18493a3f9fb1628bfc +Author: Albert Astals Cid +Date: Sat Dec 7 16:20:18 2024 +0100 + + CI: Enable readability-duplicate-include + + .clang-tidy | 2 +- + poppler/Annot.cc | 1 - + poppler/CairoRescaleBox.cc | 1 - + poppler/Form.h | 1 - + poppler/Gfx.cc | 1 - + poppler/PDFDoc.cc | 1 - + poppler/Page.cc | 1 - + utils/HtmlOutputDev.cc | 1 - + 8 files changed, 1 insertion(+), 8 deletions(-) + +commit 505dde15d707e5a3c002a998e39ecbfab14a1811 +Author: Albert Astals Cid +Date: Sat Dec 7 16:17:36 2024 +0100 + + CI: Enable readability-container-size-empty + + .clang-tidy | 2 +- + cpp/poppler-document.cpp | 2 +- + cpp/poppler-global.cpp | 4 ++-- + cpp/poppler-toc.cpp | 4 ++-- + poppler/Annot.cc | 6 +++--- + poppler/CIDFontsWidthsBuilder.h | 5 +++-- + poppler/CachedFile.cc | 10 +++++----- + poppler/CairoOutputDev.cc | 2 +- + poppler/DateInfo.cc | 5 ++--- + poppler/DistinguishedNameParser.h | 2 +- + poppler/Gfx.cc | 4 ++-- + poppler/GfxFont.cc | 4 ++-- + poppler/Link.cc | 4 ++-- + poppler/MarkedContentOutputDev.h | 4 ++-- + poppler/PreScanOutputDev.cc | 2 +- + poppler/TextOutputDev.cc | 4 ++-- + qt5/src/poppler-annotation.cc | 4 ++-- + qt5/src/poppler-document.cc | 6 ++---- + qt5/tests/check_signature_basics.cpp | 4 ++-- + qt6/tests/check_signature_basics.cpp | 4 ++-- + splash/SplashBitmap.cc | 6 +++--- + utils/pdfunite.cc | 4 ++-- + 22 files changed, 45 insertions(+), 47 deletions(-) + +commit 03b6d265562815042ce92f81ed8d8152e4390073 +Author: Albert Astals Cid +Date: Sat Dec 7 16:08:14 2024 +0100 + + CI: Enable readability-const-return-type + + .clang-tidy | 2 +- + poppler/StructElement.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 3cf2f130cddda65609e1068913fa588a9001f476 +Author: Albert Astals Cid +Date: Sat Dec 7 16:05:09 2024 +0100 + + CI: Enable readability-redundant-smartptr-get + + .clang-tidy | 2 +- + cpp/poppler-document.cpp | 28 ++++++++++++++-------------- + cpp/poppler-image.cpp | 2 +- + cpp/tests/poppler-dump.cpp | 2 +- + cpp/tests/poppler-render.cpp | 4 ++-- + utils/pdfinfo.cc | 4 ++-- + 6 files changed, 21 insertions(+), 21 deletions(-) + +commit 117500ad77aa3ada6f3aa5fc064280c89be41e66 +Author: Albert Astals Cid +Date: Sat Dec 7 16:03:31 2024 +0100 + + CI: Enable readability-redundant-string-cstr + + .clang-tidy | 2 +- + cpp/poppler-font-private.h | 6 +++--- + utils/HtmlFonts.cc | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +commit 4512018f29b2f962efc1337b7adf5c7041943c91 +Author: Albert Astals Cid +Date: Sat Dec 7 16:01:26 2024 +0100 + + CI: Enable readability-non-const-parameter + + .clang-tidy | 2 +- + glib/demo/layers.c | 2 +- + splash/Splash.cc | 2 +- + splash/SplashXPath.cc | 2 +- + splash/SplashXPath.h | 2 +- + splash/SplashXPathScanner.cc | 2 +- + splash/SplashXPathScanner.h | 2 +- + utils/pdftocairo.cc | 2 +- + 8 files changed, 8 insertions(+), 8 deletions(-) + +commit cbf0dfab819aa4c77a8965669e6e61937c669392 +Author: Albert Astals Cid +Date: Sat Dec 7 15:58:35 2024 +0100 + + CI: Enable readability-redundant-member-init + + .clang-tidy | 2 +- + glib/poppler-attachment.cc | 2 +- + poppler/Form.cc | 2 +- + poppler/StructElement.cc | 4 +--- + qt5/src/poppler-annotation.cc | 24 ++++++++++++------------ + qt5/src/poppler-form.cc | 2 +- + qt5/src/poppler-pdf-converter.cc | 2 +- + qt5/src/poppler-ps-converter.cc | 14 +------------- + qt5/tests/stress-threads-qt5.cpp | 2 +- + qt6/src/poppler-annotation.cc | 26 +++++++++++++------------- + qt6/src/poppler-form.cc | 2 +- + qt6/src/poppler-pdf-converter.cc | 2 +- + qt6/src/poppler-ps-converter.cc | 14 +------------- + qt6/tests/stress-threads-qt6.cpp | 2 +- + 14 files changed, 37 insertions(+), 63 deletions(-) + +commit 44edb44d9e0f2cb5d42d4a3099a8a78dd876b288 +Author: Albert Astals Cid +Date: Sat Dec 7 15:49:55 2024 +0100 + + CI: Enable all clang-tidy readibility checks... + + ...except the lots that fail + + .clang-tidy | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 9a02bbdd658ca2a462f33904de470375b94a1e94 +Author: Albert Astals Cid +Date: Sat Dec 7 15:47:30 2024 +0100 + + CI: Reorder a bit the clang tidy checks + + First we the enables by group, then the disables by group + + .clang-tidy | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 69acc3e897f7237b3c851c9a36a11743fd84d355 +Author: Albert Astals Cid +Date: Sat Dec 7 09:05:47 2024 +0100 + + CI: Enable performance-no-int-to-ptr + + Add exception for iconv_t checking validity since it's the recommended + way to do it according to documentation + + .clang-tidy | 2 +- + cpp/poppler-global.cpp | 5 ++++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +commit 03571e1916835ea235008d584c0ed3406bcc5b4a +Author: Albert Astals Cid +Date: Sat Dec 7 02:07:44 2024 +0100 + + CI: Enable bugprone-sizeof-expression + + .clang-tidy | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 93293183213cdf98794b708403dcc8c4d03daa18 +Author: Albert Astals Cid +Date: Sat Dec 7 01:11:59 2024 +0100 + + CI: Enable bugprone-macro-parentheses exception + + .clang-tidy | 2 +- + cpp/poppler-private.h | 4 ++-- + glib/poppler-annot.cc | 2 +- + glib/poppler-private.h | 2 +- + goo/gstrtod.cc | 4 ++-- + poppler/GfxState.cc | 2 +- + poppler/Object.h | 6 +++--- + poppler/Stream-CCITT.h | 2 +- + poppler/StructElement.cc | 6 +++--- + poppler/TextOutputDev.cc | 4 ++-- + test/gtk-test.cc | 2 +- + test/perf-test.cc | 14 +++++++------- + utils/HtmlFonts.cc | 2 +- + utils/HtmlOutputDev.h | 2 +- + 14 files changed, 27 insertions(+), 27 deletions(-) + +commit 14170dae2e0f51ea2660507bb3b958e73b2a3fc3 +Author: Albert Astals Cid +Date: Sat Dec 7 01:09:05 2024 +0100 + + CI: Enable bugprone-undefined-memory-manipulation + + Seems we don't trigger it anymore (except in Object, but we know that + works) + + .clang-tidy | 2 +- + poppler/Object.cc | 4 ++-- + poppler/Object.h | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +commit 824ad86f5cdeeb0c0675f2ff18bca7d8365ae10f +Author: Albert Astals Cid +Date: Sat Dec 7 00:45:56 2024 +0100 + + CI: Enable bugprone-suspicious-string-compare + + .clang-tidy | 2 +- + fofi/FoFiTrueType.cc | 2 +- + fofi/FoFiType1.cc | 4 ++-- + poppler/Annot.cc | 2 +- + poppler/Catalog.cc | 2 +- + poppler/GfxFont.cc | 4 ++-- + poppler/Lexer.cc | 4 ++-- + poppler/NameToCharCode.cc | 2 +- + 8 files changed, 11 insertions(+), 11 deletions(-) + +commit 51b458af5e4099a3d9cb42377776b05edc76a44e +Author: Albert Astals Cid +Date: Sat Dec 7 01:37:57 2024 +0100 + + CI: Use clang 19 + + .gitlab-ci.yml | 10 +++++----- + poppler/TextOutputDev.cc | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +commit 885e284c0c2fbbb98f757ac1fd5fe95db2355fe1 +Author: Albert Astals Cid +Date: Fri Dec 6 23:16:10 2024 +0100 + + CI: Use clang 18 + + .clang-tidy | 4 ++-- + .gitlab-ci.yml | 10 +++++----- + 2 files changed, 7 insertions(+), 7 deletions(-) + +commit 972e7a2687ac15d63f4e4c4fe74e9c0f0f81a26a +Author: Albert Astals Cid +Date: Fri Dec 6 23:50:29 2024 +0100 + + Don't convert from optional to value and back to optional + + Found by clang-tidy 18 + + poppler/CryptoSignBackend.cc | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +commit 090b0851756dfdf26650f09187feb3e55dad5d1b +Author: Albert Astals Cid +Date: Fri Dec 6 21:15:49 2024 +0100 + + GooString::copy now returns a std::unique_ptr + + goo/GooString.h | 3 +-- + goo/gfile.cc | 12 +++--------- + poppler/Annot.cc | 26 +++++++++++++------------- + poppler/CMap.cc | 8 ++++---- + poppler/CurlPDFDocBuilder.cc | 4 ++-- + poppler/FileSpec.cc | 10 +++++----- + poppler/Form.cc | 36 ++++++++++++++++++------------------ + poppler/Function.cc | 2 +- + poppler/GfxFont.cc | 4 +++- + poppler/GfxState.cc | 2 +- + poppler/GlobalParams.cc | 8 ++++---- + poppler/GlobalParamsWin.cc | 2 +- + poppler/Link.cc | 14 +++++++------- + poppler/LocalPDFDocBuilder.cc | 6 +++--- + poppler/Movie.cc | 4 ++-- + poppler/Object.cc | 6 +++--- + poppler/PDFDoc.cc | 4 ++-- + poppler/PDFDocFactory.cc | 4 ++-- + poppler/PSOutputDev.cc | 14 +++++++------- + poppler/Rendition.cc | 8 ++++---- + poppler/SecurityHandler.cc | 12 ++++++------ + poppler/StructElement.cc | 12 ++++++------ + utils/HtmlOutputDev.cc | 3 +-- + utils/pdfsig.cc | 2 +- + utils/pdftohtml.cc | 6 +++--- + utils/pdftotext.cc | 4 ++-- + 26 files changed, 105 insertions(+), 111 deletions(-) + +commit 686e3875a4e4272b35d9e72349cf1ae5855c53ca +Author: Albert Astals Cid +Date: Fri Dec 6 23:29:23 2024 +0100 + + CI: Enable bugprone-suspicious-include + + .clang-tidy | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +commit 05e0ba9a64fae58b0b8a0448daceb1daf7f75750 +Author: Albert Astals Cid +Date: Fri Dec 6 23:42:25 2024 +0100 + + test: Move .c to .h + + test/pdf-inspector.cc | 2 +- + test/{pdf-operators.c => pdf-operators.h} | 0 + 2 files changed, 1 insertion(+), 1 deletion(-) + +commit a2f4bf0706267b6ad253e2f5736cd48c731fb089 +Author: Albert Astals Cid +Date: Fri Dec 6 22:49:13 2024 +0100 + + Convert pdftohtml htmlFileName into unique_ptr + + utils/pdftohtml.cc | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +commit d85c9b539d5af8f1275d8ee7584f7c8636892d37 +Author: Albert Astals Cid +Date: Fri Dec 6 22:45:41 2024 +0100 + + Convert PSOutCustomColor name into unique_ptr + + poppler/PSOutputDev.cc | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +commit 7eb8720967f788dc6437b780d230ab4299bec57c +Author: Albert Astals Cid +Date: Fri Dec 6 22:43:32 2024 +0100 + + Convert PST1FontName psName too unique_ptr + + poppler/PSOutputDev.cc | 23 ++++++++++------------- + 1 file changed, 10 insertions(+), 13 deletions(-) + +commit beb67ddd14048b7593f1da67ed3a5d5c48ef75f5 +Author: Albert Astals Cid +Date: Fri Dec 6 22:41:37 2024 +0100 + + Convert PSOutputDev t1FontNames into a vector + + poppler/PSOutputDev.cc | 75 + ++++++++++++-------------------------------------- + poppler/PSOutputDev.h | 4 +-- + 2 files changed, 19 insertions(+), 60 deletions(-) + +commit 9d3c7088b026eeb4d85445fa63b47eb41c337fd9 +Author: Albert Astals Cid +Date: Fri Dec 6 22:07:09 2024 +0100 + + Make PSOutputDev::makePSFontName return a unique_ptr + + poppler/PSOutputDev.cc | 49 + +++++++++++++++++++++++-------------------------- + poppler/PSOutputDev.h | 2 +- + 2 files changed, 24 insertions(+), 27 deletions(-) + +commit 7084275492369b232a61d22e4baf20752ad0ccbe +Author: Albert Astals Cid +Date: Fri Dec 6 22:00:23 2024 +0100 + + Convert StructElement GooStrings to unique_ptr + + poppler/StructElement.cc | 19 +++++++------------ + poppler/StructElement.h | 38 +++++++++++++++++++------------------- + 2 files changed, 26 insertions(+), 31 deletions(-) + +commit 8c779762894ff2cd05c5806f58f1b8103ec567be +Author: Albert Astals Cid +Date: Fri Dec 6 21:55:07 2024 +0100 + + Convert StandardAuthData GooStrings to unique_ptr + + poppler/SecurityHandler.cc | 26 +++++++------------------- + 1 file changed, 7 insertions(+), 19 deletions(-) + +commit 11674ad8992e0569ea5e279df5bc0dedd3ef9a78 +Author: Albert Astals Cid +Date: Sun Feb 4 14:08:58 2024 +0100 + + CI: Use clang 17 + + .clang-tidy | 2 +- + .gitlab-ci.yml | 10 +++++----- + 2 files changed, 6 insertions(+), 6 deletions(-) + +commit 5301918792242802706e5e418fac73d76353102a +Author: Albert Astals Cid +Date: Fri Dec 6 21:46:24 2024 +0100 + + Change StandardSecurityHandler GooString to unique_ptr + + poppler/SecurityHandler.cc | 44 + +++++++++++--------------------------------- + poppler/SecurityHandler.h | 8 ++++---- + 2 files changed, 15 insertions(+), 37 deletions(-) + +commit ee4daaeed097a544d5ed46fe106cb21e0ea9d89e +Author: Albert Astals Cid +Date: Fri Dec 6 21:41:04 2024 +0100 + + Make MediaRendition GooStrings be unique_ptr + + poppler/Rendition.cc | 20 +++++--------------- + poppler/Rendition.h | 8 ++++---- + 2 files changed, 9 insertions(+), 19 deletions(-) + +commit 4b4e101fb66905fdab5afc9b59a317fe574e1a5b +Author: Albert Astals Cid +Date: Fri Dec 6 21:38:21 2024 +0100 + + Make Movie fileName a unique_ptr + + poppler/Movie.cc | 15 +++++---------- + poppler/Movie.h | 6 +++--- + 2 files changed, 8 insertions(+), 13 deletions(-) + +commit f74be6e2c71e600eeb793afca41a627e334db919 +Author: Albert Astals Cid +Date: Fri Dec 6 21:29:16 2024 +0100 + + Convert FormFieldChoice GooString ptr to unique_ptr + + poppler/Form.cc | 30 +++++++++++------------------- + poppler/Form.h | 6 +++--- + 2 files changed, 14 insertions(+), 22 deletions(-) + +commit 38868cb2955356cb2a8917bf6abaf2a6cd319ecd +Author: Albert Astals Cid +Date: Fri Dec 6 21:26:11 2024 +0100 + + Make ChoiceOpt GooStrings unique_ptr + + poppler/Form.cc | 13 ++++--------- + poppler/Form.h | 10 +++++----- + 2 files changed, 9 insertions(+), 14 deletions(-) + +commit 55f3a4434981abf0168a9d974e91bac3f62659aa +Author: Albert Astals Cid +Date: Fri Dec 6 21:22:34 2024 +0100 + + Make Make FormFieldText::internalContent a unique_ptr + + poppler/Form.cc | 11 +++-------- + poppler/Form.h | 4 ++-- + 2 files changed, 5 insertions(+), 10 deletions(-) + +commit 0a269f7a910ca1c71597c919fc31355b002c2ef7 +Author: Albert Astals Cid +Date: Fri Dec 6 21:20:47 2024 +0100 + + Make FormFieldText::defaultContent a unique_ptr + + poppler/Form.cc | 8 +++----- + poppler/Form.h | 2 +- + 2 files changed, 4 insertions(+), 6 deletions(-) + +commit 09e5a13ede95246ff99dd9f158809c18bc35be7c +Author: Albert Astals Cid +Date: Fri Dec 6 21:18:59 2024 +0100 + + Make FormFieldText::content a unique_ptr + + poppler/Form.cc | 15 ++++++--------- + poppler/Form.h | 6 +++--- + 2 files changed, 9 insertions(+), 12 deletions(-) + +commit a0002a56fa785d9ad74f22cbbfdcaeb88cd50e9d +Author: Albert Astals Cid +Date: Fri Dec 6 21:13:25 2024 +0100 + + Make Form::mappingName a unique_ptr + + poppler/Form.cc | 5 +---- + poppler/Form.h | 4 ++-- + 2 files changed, 3 insertions(+), 6 deletions(-) + +commit d193ea768b663f11494bdcac7f61eb73dbc07b4f +Author: Albert Astals Cid +Date: Fri Dec 6 21:11:43 2024 +0100 + + Make Form::alternateUiName a unique_ptr + + poppler/Form.cc | 5 +---- + poppler/Form.h | 4 ++-- + 2 files changed, 3 insertions(+), 6 deletions(-) + +commit 6e99a80b787c0ffb5f514e6a3006dd4de953f992 +Author: Albert Astals Cid +Date: Fri Dec 6 21:10:26 2024 +0100 + + Make Form::partialName a unique_ptr + + poppler/Form.cc | 10 +++------- + poppler/Form.h | 4 ++-- + 2 files changed, 5 insertions(+), 9 deletions(-) + +commit 32b893264ac95ce74d3ad0c799755be9e3e27195 +Author: Albert Astals Cid +Date: Fri Dec 6 21:04:21 2024 +0100 + + Make Form::defaultAppearance a unique_ptr + + poppler/Form.cc | 18 +++++------------- + poppler/Form.h | 10 +++++----- + 2 files changed, 10 insertions(+), 18 deletions(-) + +commit f09b2c8e3a69de46826c3fafd87b18ec16347288 +Author: Albert Astals Cid +Date: Fri Dec 6 20:53:54 2024 +0100 + + Make SysFontInfo members unique_ptr + + poppler/GlobalParams.cc | 25 +++++++++---------------- + poppler/GlobalParamsWin.cc | 5 ++--- + 2 files changed, 11 insertions(+), 19 deletions(-) + +commit 65b4510cc4fe20c44b47030008cf5eebf422b078 +Author: Albert Astals Cid +Date: Fri Dec 6 20:47:01 2024 +0100 + + Make toUnicodeDirs a vector of unique_ptr + + poppler/GlobalParams.cc | 7 ++----- + poppler/GlobalParams.h | 2 +- + 2 files changed, 3 insertions(+), 6 deletions(-) + +commit dce5dd02eebc42eda01a38c2ba4c4cc34ee15da4 +Author: Albert Astals Cid +Date: Fri Dec 6 20:42:38 2024 +0100 + + Make GfxSeparationColorSpace name be a unique_ptr + + poppler/GfxState.cc | 34 ++++++++++++++-------------------- + poppler/GfxState.h | 10 +++++----- + 2 files changed, 19 insertions(+), 25 deletions(-) + +commit 5e10c2bb63d5ea7121901419a47e2f2dad1eb181 +Author: Albert Astals Cid +Date: Fri Dec 6 20:21:15 2024 +0100 + + Make QStringToGooString return a unique_ptr + + qt5/src/poppler-document.cc | 7 +++---- + qt5/src/poppler-form.cc | 8 +++----- + qt5/src/poppler-pdf-converter.cc | 2 +- + qt5/src/poppler-private.cc | 6 +++--- + qt5/src/poppler-private.h | 4 ++-- + qt5/tests/check_strings.cpp | 4 +--- + qt6/src/poppler-annotation.cc | 4 ++-- + qt6/src/poppler-document.cc | 5 ++--- + qt6/src/poppler-form.cc | 8 +++----- + qt6/src/poppler-pdf-converter.cc | 2 +- + qt6/src/poppler-private.cc | 4 ++-- + qt6/src/poppler-private.h | 4 ++-- + qt6/tests/check_strings.cpp | 4 +--- + 13 files changed, 26 insertions(+), 36 deletions(-) + +commit 95ced79925cc49f3f69056d5d616decc63b88947 +Author: Albert Astals Cid +Date: Fri Dec 6 20:09:00 2024 +0100 + + Turn the partialFileName param into a unique_ptr + + Makes it clearer the ownenrship is being transferred + + glib/poppler-document.cc | 4 ++-- + poppler/PDFDoc.cc | 10 +++++----- + poppler/PDFDoc.h | 15 ++++++++------- + qt5/src/poppler-pdf-converter.cc | 4 ++-- + qt6/src/poppler-annotation.cc | 4 ++-- + qt6/src/poppler-pdf-converter.cc | 4 ++-- + utils/pdfsig.cc | 4 ++-- + 7 files changed, 23 insertions(+), 22 deletions(-) + +commit f8f38252489b966a0c20fe6b4aff2db2cac0918e +Author: Albert Astals Cid +Date: Fri Dec 6 19:53:40 2024 +0100 + + Make PostScriptFunction::codeString a unique_ptr + + poppler/Function.cc | 6 ++---- + poppler/Function.h | 4 ++-- + 2 files changed, 4 insertions(+), 6 deletions(-) + +commit 39b3db0f5030ab5a430edfcdd7f5f0c5cabfc762 +Author: Albert Astals Cid +Date: Fri Dec 6 19:43:54 2024 +0100 + + Make FileSpec fileName/desc/platformFilename be a unique_ptr + + poppler/FileSpec.cc | 18 ++++++------------ + poppler/FileSpec.h | 12 ++++++------ + 2 files changed, 12 insertions(+), 18 deletions(-) + +commit 57cd4f4f521c9ac6381d94cff7c80f12bddeb63c +Author: Albert Astals Cid +Date: Fri Dec 6 19:32:49 2024 +0100 + + Make collectionA and cMapNameA be const & instead of pointer + + poppler/CMap.cc | 42 +++++++++++++++++++----------------------- + poppler/CMap.h | 10 +++++----- + poppler/GfxFont.cc | 6 +++--- + poppler/GlobalParams.cc | 8 ++++---- + poppler/GlobalParams.h | 4 ++-- + 5 files changed, 33 insertions(+), 37 deletions(-) + +commit 4798cfb0a3e1e40bf962679ec337b9b6cd085f2f +Author: Albert Astals Cid +Date: Fri Dec 6 19:22:03 2024 +0100 + + Make CMap collection and cMapName be unique_ptr + + goo/GooString.h | 3 ++- + poppler/CMap.cc | 30 +++++++++++------------------- + poppler/CMap.h | 14 +++++++------- + 3 files changed, 20 insertions(+), 27 deletions(-) + +commit f5dd3e41a6a3a87e1f1438776e7a371992f535fe +Author: Albert Astals Cid +Date: Fri Dec 6 16:05:44 2024 +0000 + + More std::ranges + + poppler/Annot.cc | 2 +- + poppler/DistinguishedNameParser.h | 3 ++- + poppler/GlobalParams.cc | 8 ++++---- + poppler/PDFDoc.cc | 2 +- + poppler/TextOutputDev.cc | 2 +- + qt5/tests/check_signature_basics.cpp | 4 ++-- + qt6/tests/check_signature_basics.cpp | 4 ++-- + splash/SplashFontEngine.cc | 2 +- + 8 files changed, 14 insertions(+), 13 deletions(-) + +commit 053acd5d2b310c696993dc97072d1f2b3b1dd7ac +Author: Nelson Benítez León +Date: Fri Apr 5 15:08:09 2024 +0100 + + Don't update pdfsig.pot when POT-Creation-date is the only change + + Everytime we perform a Poppler build the pdfsig.pot file is + updated with just a new timestamp in POT-Creation-date field. + + That is a nuance as we have to always discard that change in Git + prior to submitting a MR or just switching branches locally. + + - Fix it by only updating pdfsig.pot when there are changes + other than the POT-Creation-date. Currently only done for + platforms other than Windows as it requires 'diff' command. + + - Change the OUTPUT/DEPENDS to be pdfsig.pot.new instead of + pdfsig.pot, which is more correct as the former is auto + generated while the later is a repo checked-in file, this + way we avoid pdfsig.pot from being marked as deleted by + git when doing a 'make clean'. + + Other changes: + - Drop flag '-s' from msgmerge as it's deprecated, use + instead sort flag '-F' for both msgmerge and xgettext + so their output is more synced and so prevent useless + diffs that only carry reordering changes. + + - Drop redundant flag '--language=C++' as xgettext will + use that anyway by recognizing source extension '.cc' + + - Update 'pdfsig.pot' and 'ca/pdfsig.po' with the outputs + generated with the new flags. + + Some related info: + https://savannah.gnu.org/bugs/?49654#comment1 + + Fixes #1479 + + utils/po/CMakeLists.txt | 24 +++++++++++++++++------- + utils/po/ca/pdfsig.po | 10 ++++++---- + utils/po/pdfsig.pot | 6 ++++-- + 3 files changed, 27 insertions(+), 13 deletions(-) + +commit 8b761303a9c55e238a140a45997433a77439f754 +Author: Sune Vuorela +Date: Fri Dec 6 15:41:53 2024 +0000 + + Signature is binary. Store in vector of bytes + + poppler/CryptoSignBackend.h | 3 +-- + poppler/Form.cc | 37 + +++++++++++++++--------------------- + poppler/Form.h | 10 +++++----- + poppler/GPGMECryptoSignBackend.cc | 6 +++--- + poppler/GPGMECryptoSignBackend.h | 2 +- + poppler/NSSCryptoSignBackend.cc | 4 ++-- + poppler/NSSCryptoSignBackend.h | 2 +- + qt5/src/poppler-form.cc | 4 ++-- + qt5/tests/check_signature_basics.cpp | 12 ++++++------ + qt6/src/poppler-form.cc | 4 ++-- + qt6/tests/check_signature_basics.cpp | 12 ++++++------ + utils/pdfsig.cc | 8 ++++---- + 12 files changed, 48 insertions(+), 56 deletions(-) + +commit 845d84c407c90cd0791926796c65467af0bb5207 +Author: Albert Astals Cid +Date: Fri Dec 6 16:05:42 2024 +0100 + + More std::ranges::sort + + fofi/FoFiTrueType.cc | 4 ++-- + poppler/Catalog.cc | 2 +- + poppler/Gfx.cc | 6 +++--- + 3 files changed, 6 insertions(+), 6 deletions(-) + +commit b108fa1978c504e84ee9c09d93d1ca64bcac14a3 +Author: Albert Astals Cid +Date: Fri Dec 6 15:30:07 2024 +0100 + + Make HtmlLink take a unique_ptr + + utils/HtmlLinks.cc | 14 +++++--------- + utils/HtmlLinks.h | 4 ++-- + utils/HtmlOutputDev.cc | 4 ++-- + 3 files changed, 9 insertions(+), 13 deletions(-) + +commit e323e1b76a01156e9372c2a3beabb22e9aeecff4 +Author: Albert Astals Cid +Date: Fri Dec 6 15:29:04 2024 +0100 + + Make HtmlOutputDev::getLinkDest return unique_ptr + + utils/HtmlOutputDev.cc | 27 ++++++++++++--------------- + utils/HtmlOutputDev.h | 4 ++-- + 2 files changed, 14 insertions(+), 17 deletions(-) + +commit 0fcb26cef8a334807683c67ca6b4c7a41c609517 +Author: Albert Astals Cid +Date: Fri Dec 6 15:19:40 2024 +0100 + + Make HtmlLink::getLinkStart return unique_ptr + + utils/HtmlLinks.cc | 6 +++--- + utils/HtmlLinks.h | 2 +- + utils/HtmlOutputDev.cc | 17 +++++++---------- + 3 files changed, 11 insertions(+), 14 deletions(-) + +commit 6ef8ae1ca07c464ffb134217bbe5b948e6a2c20c +Author: Albert Astals Cid +Date: Fri Dec 6 15:15:18 2024 +0100 + + Remove two unused functions + + utils/HtmlLinks.h | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +commit 2c14cd46ffa710714cdcc3815e4ecf479c0229dc +Author: Albert Astals Cid +Date: Fri Dec 6 15:10:50 2024 +0100 + + Make getReusablePath return a unique_ptr + + poppler/Gfx.cc | 8 +++----- + poppler/GfxState.h | 2 +- + 2 files changed, 4 insertions(+), 6 deletions(-) + +commit e5e255c89e84da304649b0daca7c187aa9836f17 +Author: Albert Astals Cid +Date: Thu Jun 6 22:58:21 2024 +0200 + + Use the better sort function + + poppler/Dict.cc | 4 ++-- + poppler/GfxFont.cc | 4 ++-- + splash/SplashXPathScanner.cc | 4 ++-- + utils/printencodings.cc | 4 ++-- + 4 files changed, 8 insertions(+), 8 deletions(-) + +commit 4f908d903fd03d602b492b87ce8afd9e12afb316 +Author: Albert Astals Cid +Date: Fri Dec 6 12:36:24 2024 +0100 + + AnnotAppearanceBuilder::setDrawColor take a const & instead of + a pointer + + We don't change it and we don't allow it to be nullptr + + poppler/Annot.cc | 64 + ++++++++++++++++++++++++++++---------------------------- + poppler/Annot.h | 2 +- + 2 files changed, 33 insertions(+), 33 deletions(-) + +commit e50c68acbf15272e7703a962e7748600b9540793 +Author: Albert Astals Cid +Date: Fri Dec 6 12:33:17 2024 +0100 + + AnnotAppearanceBuilder::setLineStyleForBorder take const & instead + of pointer + + We don't change it and we don't allow it to be nullptr + + poppler/Annot.cc | 20 ++++++++++---------- + poppler/Annot.h | 2 +- + 2 files changed, 11 insertions(+), 11 deletions(-) + +commit e80adad0acfc05bd35eb74dfd2b82304e3ee5f94 +Author: Albert Astals Cid +Date: Fri Dec 6 12:28:29 2024 +0100 + + Annot::setRect take a const & instead of pointer + + We don't change it and we don't allow it to be nullptr + + poppler/Annot.cc | 4 ++-- + poppler/Annot.h | 2 +- + qt5/src/poppler-annotation.cc | 2 +- + qt6/src/poppler-annotation.cc | 2 +- + 4 files changed, 5 insertions(+), 5 deletions(-) + +commit c8da9686d0d484a8bcdf9c7228ff8724138b56a1 +Author: Albert Astals Cid +Date: Fri Dec 6 12:25:48 2024 +0100 + + Update (C) + + poppler/Annot.cc | 2 +- + poppler/Annot.h | 1 + + poppler/UTF.h | 2 +- + qt5/src/poppler-annotation-private.h | 1 + + qt5/src/poppler-annotation.cc | 1 + + qt6/src/poppler-annotation-private.h | 1 + + 6 files changed, 6 insertions(+), 2 deletions(-) + +commit b41f7b54aa975c4fc0d5c726386c8d6a0cdd0f5e +Author: Albert Astals Cid +Date: Fri Dec 6 12:14:10 2024 +0100 + + Make AnnotTextMarkup::setQuadrilaterals take a const & instead of + a pointer + + We don't change it and don't allow it to be null. + + Brought a memory leak into view in the glib poppler-page.cc + + glib/poppler-annot.cc | 2 +- + glib/poppler-page.cc | 16 +++++++++------- + poppler/Annot.cc | 20 ++++++++++---------- + poppler/Annot.h | 2 +- + qt5/src/poppler-annotation.cc | 2 +- + qt6/src/poppler-annotation.cc | 2 +- + 6 files changed, 23 insertions(+), 21 deletions(-) + +commit fe2eb156317a2a37664cb271b4bd9232a7b1fc52 +Author: Albert Astals Cid +Date: Fri Dec 6 12:09:30 2024 +0100 + + Make AnnotQuadrilaterals::getXXXX const + + poppler/Annot.cc | 16 ++++++++-------- + poppler/Annot.h | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +commit a80ff0ddf7a185c93beaa7f206df2b4d8c35b5a1 +Author: Albert Astals Cid +Date: Fri Dec 6 12:07:28 2024 +0100 + + AnnotInk::setInkList make param be const & instead of && + + We're only reading it so don't adopt it + + poppler/Annot.cc | 2 +- + poppler/Annot.h | 2 +- + qt5/src/poppler-annotation.cc | 4 ++-- + qt6/src/poppler-annotation.cc | 4 ++-- + 4 files changed, 6 insertions(+), 6 deletions(-) + +commit f5485fbf786431d8ffdb9a6d30b51da66d8792aa +Author: Albert Astals Cid +Date: Fri Dec 6 12:05:18 2024 +0100 + + AnnotPolygon::setVertices take a const & instead a pointer + + We don't changed it and we don't allow it to be nullptr + + poppler/Annot.cc | 8 ++++---- + poppler/Annot.h | 4 ++-- + qt5/src/poppler-annotation.cc | 6 +++--- + qt6/src/poppler-annotation.cc | 6 +++--- + 4 files changed, 12 insertions(+), 12 deletions(-) + +commit 41ba755f1c4cf787799227856b716a3010d42835 +Author: Sune Vuorela +Date: Tue Nov 19 10:28:01 2024 +0100 + + Annots: memory handling cleanup + + poppler/Annot.cc | 210 + ++++++++++++----------------------- + poppler/Annot.h | 21 ++-- + qt5/src/poppler-annotation-private.h | 2 +- + qt5/src/poppler-annotation.cc | 41 +++---- + qt6/src/poppler-annotation-private.h | 2 +- + qt6/src/poppler-annotation.cc | 42 +++---- + 6 files changed, 111 insertions(+), 207 deletions(-) + +commit 32206c8c629320bf60e108fc57e45724cd6895ff +Author: Albert Astals Cid +Date: Mon Dec 2 00:05:36 2024 +0100 + + pdfdetach: Make -savefile work with Unicode strings + + Fixes #1540 + + poppler/UTF.h | 6 +++ + utils/pdfdetach.cc | 113 + +++++++++++++++++++++-------------------------------- + 2 files changed, 50 insertions(+), 69 deletions(-) + +commit 9a04cb1e464d32bc171610fc49d02dd4e4d4a5b6 +Author: Albert Astals Cid +Date: Fri Dec 6 11:02:43 2024 +0100 + + Update (C) + + poppler/Object.h | 1 + + poppler/Parser.cc | 1 + + 2 files changed, 2 insertions(+) + +commit bf9926d65954c0fe91237fdc30023e3fdd8a61cc +Author: Albert Astals Cid +Date: Fri Dec 6 01:24:52 2024 +0100 + + CI: Use android-qt68 image + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 05e8eea835b8318d054862a2528d1fd31b9513dc +Author: Sune Vuorela +Date: Wed Dec 4 11:28:15 2024 +0100 + + Object: create from std::unique_ptr + + We have plenty of places where a GooString could have been in a + unique_ptr, then later on passed on to Object, but isn't. + + This is convenience API additions, no internal changes to how Object + manages its internals. + + poppler/Object.h | 6 ++++++ + poppler/Parser.cc | 2 +- + 2 files changed, 7 insertions(+), 1 deletion(-) + +commit 30eada0d2bceb42c2d2a87361339063e0b9bea50 +Author: Albert Astals Cid +Date: Mon Dec 2 22:21:42 2024 +0100 + + poppler 24.12.0 + + CMakeLists.txt | 4 ++-- + NEWS | 25 ++++++++++++++++++++++++- + cpp/Doxyfile | 2 +- + glib/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 31 insertions(+), 8 deletions(-) + +commit 5a640ff30fc2028288430f0cc52822ac14040b76 +Author: Sune Vuorela +Date: Mon Dec 2 16:39:34 2024 +0000 + + Form: fclose file on signature error + + poppler/Form.cc | 1 + + 1 file changed, 1 insertion(+) + +commit eb6a860eb5646a8be97c7fa3354798d6b88e80ab +Author: Albert Astals Cid +Date: Sun Dec 1 02:45:30 2024 +0100 + + Update (C) + + poppler/Annot.cc | 1 + + poppler/Annot.h | 1 + + poppler/CertificateInfo.cc | 2 +- + poppler/CertificateInfo.h | 2 +- + poppler/CryptoSignBackend.cc | 2 +- + 5 files changed, 5 insertions(+), 3 deletions(-) + +commit 514623e7a02239aa0c1f47c907ae8719910282ca +Author: Sune Vuorela +Date: Tue Apr 16 11:32:45 2024 +0200 + + Mark certificates that can do qualified signing + + Certain signatures can do qualified signing (legally binding). Ensure + that they can be properly tagged in various UI's. + + poppler/CertificateInfo.cc | 12 +++++++++++- + poppler/CertificateInfo.h | 3 +++ + poppler/GPGMECryptoSignBackend.cc | 2 ++ + qt6/src/poppler-form.cc | 8 ++++++++ + qt6/src/poppler-form.h | 7 +++++++ + utils/pdfsig.cc | 2 +- + 6 files changed, 32 insertions(+), 2 deletions(-) + +commit 144890650d76058acddeba0350e1f62cf8464bf8 +Author: Lucas Baudin +Date: Sun Nov 24 18:20:51 2024 +0100 + + Generate appearance stream for annotation stamp when it is not + available (i.e. was invalidated), taking into account the custom + image case + + poppler/Annot.cc | 53 + +++++++++++++++++++++++++++++++++++------------------ + poppler/Annot.h | 3 +++ + 2 files changed, 38 insertions(+), 18 deletions(-) + +commit 89ac0c4450130d8a3649bb3e1f392148e9d5e57e +Author: Sune Vuorela +Date: Thu Nov 28 08:24:57 2024 +0000 + + CryptoSign: Allow backends to support different signature types + + poppler/CryptoSignBackend.cc | 29 ++++++++++++++++++++++++++++ + poppler/CryptoSignBackend.h | 16 +++++++++++++++- + poppler/Form.cc | 37 + ++++++++++++++++++------------------ + poppler/Form.h | 22 +++++++-------------- + poppler/GPGMECryptoSignBackend.cc | 18 ++++++++++++++++-- + poppler/GPGMECryptoSignBackend.h | 3 ++- + poppler/NSSCryptoSignBackend.cc | 13 +++++++++++-- + poppler/NSSCryptoSignBackend.h | 3 ++- + qt5/src/poppler-form.cc | 12 ++++++------ + qt5/tests/check_signature_basics.cpp | 8 ++++---- + qt6/src/poppler-form.cc | 12 ++++++------ + qt6/tests/check_signature_basics.cpp | 8 ++++---- + utils/pdfsig.cc | 15 ++++++++------- + 13 files changed, 129 insertions(+), 67 deletions(-) + +commit 945a32731327ae2547753c6802aa51f932d129af +Author: Albert Astals Cid +Date: Thu Nov 28 00:01:35 2024 +0100 + + Update (C) + + poppler/Parser.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 36c171a149c3df9c946a6397ce6599d60fc209a9 +Author: Sune Vuorela +Date: Mon Nov 25 11:21:19 2024 +0100 + + pdfsig.1: Extend GPG backend documentation + + closes: #1535 + + utils/pdfsig.1 | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit 2160415175ee89838f84a142f3f85e2698ce0c5d +Author: Nelson Benítez León +Date: Sun Nov 10 17:40:49 2024 +0000 + + Parser.cc: remove superfluous condition inside 'if' + + getObj() checks recursionLimit and returns ObjError if reached, + so in that case both obj2.isError() and "recursion + 1 >= + recursionLimit" + are true, but that also means that obj2.isError() is superfluous + in the 'if' statement, because if "recursion + 1 >= recursionLimit" + is true it means previous getObj() must have returned a objError + (because they use the same recursion check). + + Brought up here[1] + + [1] + https://gitlab.freedesktop.org/poppler/poppler/-/issues/1508#note_2651715 + + poppler/Parser.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 547babe90b332c5df3048abfdd6b286a663ce26a +Author: Albert Astals Cid +Date: Thu Nov 21 22:38:33 2024 +0100 + + Update (C) + + cpp/poppler-document-private.h | 1 + + cpp/poppler-image.cpp | 1 + + cpp/poppler-private.cpp | 1 + + poppler/Error.cc | 1 + + poppler/JPXStream.cc | 1 + + poppler/PDFDoc.h | 2 +- + poppler/Page.cc | 1 + + poppler/Page.h | 1 + + poppler/SecurityHandler.cc | 1 + + qt5/src/poppler-pdf-converter.cc | 1 + + qt6/src/poppler-annotation.h | 1 + + qt6/src/poppler-pdf-converter.cc | 1 + + utils/pdfattach.cc | 1 + + 13 files changed, 13 insertions(+), 1 deletion(-) + +commit 0a121fc2ad056d33a8084d2d05a9104916f5fcea +Author: Sune Vuorela +Date: Thu Nov 21 09:36:58 2024 +0100 + + Try verify public private headers + + .gitlab-ci.yml | 2 +- + CMakeLists.txt | 39 + +++++++++++++++++++++++++++++++-------- + cpp/poppler-document-private.h | 2 +- + cpp/poppler-image.cpp | 10 +++++----- + cpp/poppler-private.cpp | 2 +- + poppler/DateInfo.cc | 4 ++-- + poppler/Error.cc | 2 +- + poppler/JPXStream.cc | 2 +- + poppler/SecurityHandler.cc | 2 +- + utils/HtmlOutputDev.cc | 2 +- + utils/pdfattach.cc | 2 +- + 11 files changed, 46 insertions(+), 23 deletions(-) + +commit 883fbaaa5d1cf12135b2ded2aa732fed0d9af35a +Author: Pablo Correa Gómez +Date: Thu Nov 21 12:18:46 2024 +0100 + + README: update the location of the official poppler room + + And inform that it's bridged to matrix + + README.contributors | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 54b1b0360a59adf3516e39dbef6f519e09eefbba +Author: Nicolas Fella +Date: Thu Nov 21 00:15:41 2024 +0100 + + Install CryptoSignBackend header + + It's used from other private but installed headers + + CMakeLists.txt | 1 + + 1 file changed, 1 insertion(+) + +commit 2b4d8631989c6fe29616ed368f370cf11bb9472c +Author: Sune Vuorela +Date: Sat Nov 16 20:35:22 2024 +0100 + + Debug feature for cryptographic signatures + + While debugging, it is nice to be able to extract both signatures and + the data the signature is supposed to be over. + + poppler/GPGMECryptoSignBackend.cc | 13 +++++++++++++ + poppler/GPGMECryptoSignBackend.h | 5 +++++ + 2 files changed, 18 insertions(+) + +commit 69c8cd7d4f830056ddce8b9f7ec6568d52c5534e +Author: Sune Vuorela +Date: Mon Oct 14 10:17:57 2024 +0200 + + Better signing error handling + + Just having a bool for many different failure modes is a bit hard to + work with, so now we have an enum for the new API, so let's extend it + with some of the error possibilities + + glib/poppler-document.cc | 14 ++++++---- + poppler/CryptoSignBackend.h | 15 ++++++++-- + poppler/Form.cc | 58 + ++++++++++++++++++++------------------- + poppler/Form.h | 12 ++++---- + poppler/GPGMECryptoSignBackend.cc | 11 ++++++-- + poppler/GPGMECryptoSignBackend.h | 2 +- + poppler/NSSCryptoSignBackend.cc | 31 +++++++++++---------- + poppler/NSSCryptoSignBackend.h | 2 +- + poppler/PDFDoc.cc | 16 ++++++----- + poppler/PDFDoc.h | 8 ++++-- + qt5/src/poppler-form.cc | 9 +++--- + qt5/src/poppler-pdf-converter.cc | 7 +++-- + qt6/src/poppler-annotation.cc | 8 ++++++ + qt6/src/poppler-annotation.h | 8 ++++-- + qt6/src/poppler-form.cc | 23 ++++++++++++---- + qt6/src/poppler-form.h | 10 +++++-- + qt6/src/poppler-pdf-converter.cc | 7 +++-- + utils/pdfsig.cc | 8 +++--- + 18 files changed, 154 insertions(+), 95 deletions(-) + +commit 87aaa2392cbbd3ec30fb5b06e336f0dec91031a9 +Author: Sune Vuorela +Date: Mon Nov 18 16:00:10 2024 +0100 + + createGfx: use unique pointer for creation + + glib/poppler-page.cc | 21 +++++++++------------ + poppler/Page.cc | 13 ++++--------- + poppler/Page.h | 3 ++- + 3 files changed, 15 insertions(+), 22 deletions(-) + +commit ce1cab429a7dfcb355c771a4d9b8e4d964a6560d +Author: Sune Vuorela +Date: Mon Nov 18 10:40:17 2024 +0100 + + Signature GpgME backend: Add Kyber to switch + + poppler/GPGMECryptoSignBackend.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit 26e52dc0dbf00e3b6b0bccb809736f7a3e4d29f5 +Author: Sune Vuorela +Date: Sat Nov 16 20:39:38 2024 +0100 + + GfxFont: memory cleanup + + poppler/GfxFont.cc | 44 ++++++++++++++------------------------------ + poppler/GfxFont.h | 14 +++++++------- + 2 files changed, 21 insertions(+), 37 deletions(-) + +commit 92cf4406b1c41d3f59aa5c80dd8f761d0ef61a18 +Author: Albert Astals Cid +Date: Fri Nov 15 17:58:46 2024 +0100 + + Simplify getting the Font dict + + We want to both get the Dict object and if it is a Ref, get its ref + because we use it later. + + Previously we did lookup the Font Object from the dict without + fetching + (NF), and if it was a Ref, we had to fetch to get the actual Dict, + and if it + was a Dict then all was good. + + New code uses the "new" lookup function that returns the Ref as an + output parameter, so we are doing exactly the same but the code + is much + simpler + + poppler/FontInfo.cc | 25 +++++++------------------ + poppler/Gfx.cc | 13 ++++--------- + poppler/GfxFont.cc | 6 +++--- + poppler/GfxFont.h | 2 +- + poppler/PSOutputDev.cc | 25 ++++++------------------- + 5 files changed, 21 insertions(+), 50 deletions(-) + +commit 954498825c2c7de75447267827599a5acf80004b +Author: Albert Astals Cid +Date: Fri Nov 15 17:47:01 2024 +0100 + + Update (C) + + poppler/Gfx.cc | 2 +- + poppler/Gfx.h | 2 +- + poppler/GfxFont.h | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit 7a786194a979481637d5b6aaa02779123d0e2c7a +Author: Oliver Sander +Date: Tue Oct 29 17:22:48 2024 +0100 + + GfxFontDict: Constify a pointer argument + + poppler/GfxFont.cc | 2 +- + poppler/GfxFont.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 00d81af686a2ea9a69e6af06e280c801d9bea5d6 +Author: Oliver Sander +Date: Tue Oct 29 17:22:20 2024 +0100 + + Gfx.cc: Reduce scope of local variable + + poppler/Gfx.cc | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +commit 947ec511b8049929d4435c86a3069757fdbb1062 +Author: Oliver Sander +Date: Tue Oct 29 17:18:24 2024 +0100 + + GfxResources: Store 'fonts' in a std::unique_ptr + + poppler/Gfx.cc | 10 +++------- + poppler/Gfx.h | 2 +- + 2 files changed, 4 insertions(+), 8 deletions(-) + +commit 30dc5eddbbe623f9b5c7e467583397a424e7ca9a +Author: Nicolas Fella +Date: Mon Sep 16 17:30:28 2024 +0200 + + Handle font size 0 in AnnotAppearanceBuilder::drawSignatureFieldText + + 0 means 'choose the size according to available space', like we do + elsewhere + + poppler/Annot.cc | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +commit 2d1eea8df83e2471f656337077b89fb85d0d1856 +Author: Nicolas Fella +Date: Wed Oct 23 22:54:20 2024 +0200 + + Set border on signature annotation before generating appearance + + The appearance depends on the border + + poppler/PDFDoc.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit b018b989a10dbdc6d56f0d80ea9a4e54911fa407 +Author: Albert Astals Cid +Date: Thu Nov 14 21:20:53 2024 +0100 + + Update (C) + + poppler/GfxFont.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 9e3f5b5abda5c6bb4ad94de3fe16f138dfdca07b +Author: G B +Date: Thu Nov 14 20:19:18 2024 +0000 + + Override encoding for Zapf Dingbats + + poppler/GfxFont.cc | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +commit f0cf34efea946001d2ee2f7cb501fae625cc7cd5 +Author: Albert Astals Cid +Date: Wed Nov 13 22:28:47 2024 +0100 + + Fix build with in-tree build + + CMakeLists.txt | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +commit fabce6e1d53af474cfc5bca3773e6af3bd748f33 +Author: Albert Astals Cid +Date: Wed Nov 13 22:24:23 2024 +0100 + + Update (C) + + poppler/StructTreeRoot.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 0bcf96f3312d4fa3c06f30dc37f033aa814de9ca +Author: Albert Astals Cid +Date: Wed Nov 13 22:07:08 2024 +0100 + + export StructTreeRoot so we make it clear it is used/usable + + poppler/StructTreeRoot.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2d7188a3a3679a359e3eb49beb9657027a16b3bd +Author: Albert Astals Cid +Date: Wed Nov 13 22:06:46 2024 +0100 + + Install StructTreeRoot.h again + + I didn't realize it was usable without being exported + + CMakeLists.txt | 1 + + 1 file changed, 1 insertion(+) + +commit 5ec4d46373277e5a7a90d7fe2f85edbc1d857da7 +Author: Albert Astals Cid +Date: Wed Nov 13 21:20:27 2024 +0100 + + Add a few exports more to classes that can be used from other + exported classes + + splash/SplashFont.h | 5 +++-- + splash/SplashFontFile.h | 4 ++-- + 2 files changed, 5 insertions(+), 4 deletions(-) + +commit 6a87cf2157e6ce5b1f98288727e8fc2a2caf0220 +Author: Albert Astals Cid +Date: Wed Nov 13 21:15:55 2024 +0100 + + Install SplashFont again + + I failed realizing it was usable + + CMakeLists.txt | 1 + + 1 file changed, 1 insertion(+) + +commit 7b30dc62111d354132c169515a1cd3137fc027c7 +Author: Albert Astals Cid +Date: Mon Nov 11 00:17:43 2024 +0100 + + Update (C) + + poppler/CairoOutputDev.h | 1 + + poppler/GfxState.cc | 1 + + poppler/GfxState.h | 1 + + poppler/JPEG2000Stream.cc | 1 + + poppler/JPEG2000Stream.h | 1 + + poppler/JPXStream.cc | 1 + + poppler/JPXStream.h | 1 + + poppler/OutputDev.h | 1 + + poppler/PSOutputDev.cc | 1 + + poppler/Stream.h | 1 + + 10 files changed, 10 insertions(+) + +commit 30117b1c9cc016236689877639813a9e4187be39 +Author: Albert Astals Cid +Date: Sun Nov 10 23:57:37 2024 +0100 + + Add export to ViewerPreferences + + There's people sadly using this class on the wild so make it + clear it's used otherwise we will make it not installed + again in the future + + poppler/ViewerPreferences.h | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit b9620f70a41e878b7f4e615ced4bd7ecf9fe7233 +Author: Albert Astals Cid +Date: Sun Nov 10 23:47:18 2024 +0100 + + Install ViewerPreferences again + + I failed realizing it was usable given it's all inline functions + + CMakeLists.txt | 1 + + 1 file changed, 1 insertion(+) + +commit 69c3883c6d2343246f1daa65dbbff6e289a2a7eb +Author: Nelson Benítez León +Date: Tue Oct 29 13:54:15 2024 +0000 + + cairo: Fix JPX transparency regression + + Commit 2dcfefb43c from https://bugs.freedesktop.org/93468 + fixed images 2) and 3) of file[1] (which are jpeg images + with transparency) from showing garbage to show fine + but with a solid background instead of transparency. + + But this fix interferes with our previous commit where + we are adding support to Cairo backend to correctly show + transparency in jpeg files that have it. + + So we need to partially revert commit 2dcfefb43c but only + for the Cairo backend, because the Splash backend still + uses this fix to improve some jpeg transparent images by + at least showing them with a solid background. + + So we added a new method JPXStream::setSupportJPXtransparency() + so a JPXStream can be informed that the OutputDevice is + capable of handling jpx transparency and so to not perform + the downgrade fix to solid background. + + The OutputDevice itself can be marked as handling jpx + transparency by new method OutputDev::supportJPXtransparency() + which the Cairo backend implements as TRUE. + + So now file[1] does not regress in Cairo backend, although + display changes from showing with solid background to show + with proper transparency as CairoOutputDev now supports that. + + For the Splash backend no display regressions should happen + at all, because any display changes are now guard by + OutpuDev::supportJPXtransparency() which the Splash backend + returns FALSE at this moment. + + [1] https://bugs.freedesktop.org/attachment.cgi?id=120624 + + Part of #1486 + + poppler/CairoOutputDev.h | 3 +++ + poppler/Gfx.cc | 11 ++++++++++- + poppler/JPEG2000Stream.cc | 3 ++- + poppler/JPEG2000Stream.h | 7 +++++++ + poppler/OutputDev.h | 3 +++ + 5 files changed, 25 insertions(+), 2 deletions(-) + +commit 69da0db4998f8bcac5da67f9ce11907ae657acbd +Author: Nelson Benítez León +Date: Fri Jun 21 22:57:27 2024 +0100 + + cairo: Fix rendering of JPX images with transparency + + Handle transparent JPX images, they may contain RGBA data + when no ColorSpace pdf dict is defined or when SMaskInData + is non-zero. + + PDF files posted in below issues are fixed by this commit: + https://gitlab.freedesktop.org/poppler/poppler/-/issues/1486 + https://github.com/mozilla/pdf.js/issues/16782 + https://github.com/mozilla/pdf.js/issues/11306 + https://github.com/mozilla/pdf.js/issues/17416 + + Inspired by related fix in pdf.js: + https://github.com/mozilla/pdf.js/pull/18204 + + While working on this commit we also succesfully addressed + regressions that emerged for the following files: + https://github.com/mozilla/pdf.js/issues/18896 + https://bugs.freedesktop.org/attachment.cgi?id=49294 + + Issue #1486 + + poppler/CairoOutputDev.cc | 17 +++++++++++++++-- + poppler/Gfx.cc | 44 + ++++++++++++++++++++++++++++++++++---------- + poppler/GfxState.cc | 28 +++++++++++++++++++++++++++- + poppler/GfxState.h | 23 ++++++++++++++++++++++- + poppler/JPEG2000Stream.cc | 6 +++++- + poppler/JPEG2000Stream.h | 2 +- + poppler/JPXStream.cc | 3 ++- + poppler/JPXStream.h | 2 +- + poppler/PSOutputDev.cc | 1 + + poppler/Stream.h | 2 +- + 10 files changed, 109 insertions(+), 19 deletions(-) + +commit b6a783b3954de7d1dc756c039b2bc8f9656d22f7 +Author: lbaudin1 +Date: Fri Nov 8 14:24:30 2024 +0000 + + glib: Add free text annotations support + + glib/demo/annots.c | 45 +++- + glib/poppler-annot.cc | 490 + ++++++++++++++++++++++++++++++++---- + glib/poppler-annot.h | 87 +++++++ + glib/poppler-document.cc | 6 +- + glib/poppler-private.h | 2 + + glib/poppler.h | 1 + + glib/reference/poppler-sections.txt | 33 +++ + glib/reference/poppler.types | 3 +- + 8 files changed, 613 insertions(+), 54 deletions(-) + +commit 735fb475f7b7793c3827d3b3090356503e99075c +Author: Albert Astals Cid +Date: Sat Nov 2 23:33:19 2024 +0100 + + poppler 24.11.0 + + CMakeLists.txt | 4 ++-- + NEWS | 24 ++++++++++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 29 insertions(+), 5 deletions(-) + +commit 2b5fdf3daddb8846d8df0e82acda1fe8f1dc7c81 +Author: Albert Astals Cid +Date: Sat Nov 2 23:13:01 2024 +0100 + + CI: clang-format 19 + + .gitlab-ci.yml | 4 ++-- + README.contributors | 2 +- + goo/gbase64.cc | 2 +- + poppler/CairoRescaleBox.h | 4 ++-- + poppler/JPXStream.cc | 2 +- + poppler/StructElement.cc | 17 ++++------------- + poppler/TextOutputDev.cc | 8 ++++---- + poppler/UnicodeTypeTable.cc | 8 ++++---- + splash/SplashFTFont.cc | 8 ++++---- + utils/HtmlFonts.h | 2 +- + 10 files changed, 24 insertions(+), 33 deletions(-) + +commit 5ad31b4b5532b7e68f5e0584290546c457b31111 +Author: Albert Astals Cid +Date: Sat Nov 2 22:47:04 2024 +0100 + + Update (C) + + poppler/GlobalParams.cc | 2 +- + poppler/GlobalParams.h | 1 + + poppler/SplashOutputDev.cc | 1 + + splash/SplashFTFontEngine.cc | 1 + + splash/SplashFTFontEngine.h | 1 + + splash/SplashFTFontFile.cc | 1 + + splash/SplashFTFontFile.h | 1 + + splash/SplashFontEngine.cc | 1 + + splash/SplashFontEngine.h | 1 + + splash/SplashFontFile.cc | 1 + + splash/SplashFontFile.h | 1 + + splash/SplashFontFileID.h | 1 + + 12 files changed, 12 insertions(+), 1 deletion(-) + +commit d0c1bb045c4d7c501386f239978d6404b08dc850 +Author: Albert Astals Cid +Date: Sat Nov 2 22:10:17 2024 +0000 + + CI: Fedora mingw 40 -> 41 + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit cd2ea80be31f7e4bf759639f105b05805985efb6 +Author: Nicolas Fella +Date: Tue Oct 8 18:31:20 2024 +0200 + + Increase refcount for signature annotation when creating + + Otherwise it's deleted too early + + qt6/src/poppler-annotation.cc | 2 ++ + 1 file changed, 2 insertions(+) + +commit 25035bdf85494bdc1c83f9df8228a35562a86247 +Author: Albert Astals Cid +Date: Sat Nov 2 22:45:33 2024 +0100 + + qt: respect non-ascii encodings in Launch target file names + + qt5/src/poppler-page.cc | 2 +- + qt6/src/poppler-page.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 0d06a46c70c3f49b44aea26d366cb3ab656ed269 +Author: v4hn +Date: Thu Oct 31 04:55:31 2024 +0100 + + respect non-ascii encodings in Launch target file names + + PDF2.0 (and also 1.7) - section 7.11.3 specifies the UF field + as a "text string" in the pdf sense, which might be encoded + in one of three encodings UTF16. The encoding was ignored here + before resulting in broken links when (1) the UF field was + actually used and (2) the contents was a marked UTF16 string + that would be interpreted as UTF8. + + I considered to adjust the parameters too, but these are not + officially + defined as text -either "undefined" or partly as "byte string"- + and have + been deprecated in PDF2.0. + + glib/poppler-action.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 7a34832efb042b0200c05b064442ee28b808da63 +Author: Sune Vuorela +Date: Wed Oct 30 15:22:13 2024 +0100 + + Cleanups after 9d43c190017e621f83cd + + poppler/CharCodeToUnicode.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 9d43c190017e621f83cd36a10e985aa4bc525662 +Author: Sune Vuorela +Date: Wed Oct 30 10:37:47 2024 +0100 + + CharCodeToUnicode memory handling cleanup + + Rather than do manual ref/deref and delete this, leverage + smartpointers + instead. + + poppler/CharCodeToUnicode.cc | 108 + +++++++++++-------------------------------- + poppler/CharCodeToUnicode.h | 50 ++++++++++---------- + poppler/GfxFont.cc | 31 ++++--------- + poppler/GfxFont.h | 6 +-- + poppler/GlobalParams.cc | 10 ++-- + poppler/GlobalParams.h | 6 +-- + 6 files changed, 73 insertions(+), 138 deletions(-) + +commit d04eea35c08085511829b0684e983ec9f3ab71ef +Author: Sune Vuorela +Date: Wed Oct 30 13:20:45 2024 +0000 + + Do a little less manual memory handling in splash + + poppler/SplashOutputDev.cc | 58 + +++++++++++++++++++++----------------------- + splash/SplashFTFontEngine.cc | 25 ++++++++++--------- + splash/SplashFTFontEngine.h | 13 +++++----- + splash/SplashFTFontFile.cc | 16 ++++++------ + splash/SplashFTFontFile.h | 8 +++--- + splash/SplashFontEngine.cc | 28 ++++++++++----------- + splash/SplashFontEngine.h | 15 ++++++------ + splash/SplashFontFile.cc | 5 ++-- + splash/SplashFontFile.h | 7 +++--- + splash/SplashFontFileID.h | 2 +- + 10 files changed, 89 insertions(+), 88 deletions(-) + +commit 0634a0f9b802c7626958c55a43adef37a1d0a897 +Author: Albert Astals Cid +Date: Tue Oct 29 23:28:09 2024 +0100 + + Update (C) + + fofi/FoFiTrueType.h | 2 +- + poppler/CairoFontEngine.cc | 2 +- + poppler/Form.cc | 2 +- + qt5/src/QPainterOutputDev.cc | 2 +- + qt6/src/QPainterOutputDev.cc | 2 +- + splash/SplashFTFontEngine.cc | 2 +- + splash/SplashFTFontEngine.h | 2 +- + splash/SplashFTFontFile.cc | 2 +- + splash/SplashFTFontFile.h | 2 +- + splash/SplashFontEngine.cc | 2 +- + splash/SplashFontEngine.h | 2 +- + 11 files changed, 11 insertions(+), 11 deletions(-) + +commit 94467509a013dd5cf46c942baa598f2b296571f4 +Author: Albert Astals Cid +Date: Tue Oct 22 09:50:48 2024 +0200 + + Force passing faceIndex to all functions that may need it + + I'm relatively sure that for embedded fonts it always will be 0, but + since we have the variable use it + + Fixes issue #1512 in Splash and PS output devices + + fofi/FoFiTrueType.h | 4 ++-- + poppler/CairoFontEngine.cc | 6 +++--- + poppler/Form.cc | 2 +- + poppler/PSOutputDev.cc | 36 ++++++++++++++++++------------------ + poppler/PSOutputDev.h | 12 ++++++------ + poppler/SplashOutputDev.cc | 23 +++++++++++------------ + qt5/src/QPainterOutputDev.cc | 11 +++++------ + qt6/src/QPainterOutputDev.cc | 11 +++++------ + splash/SplashFTFontEngine.cc | 20 ++++++++++---------- + splash/SplashFTFontEngine.h | 12 ++++++------ + splash/SplashFTFontFile.cc | 12 ++++++------ + splash/SplashFTFontFile.h | 6 +++--- + splash/SplashFontEngine.cc | 20 ++++++++++---------- + splash/SplashFontEngine.h | 12 ++++++------ + 14 files changed, 92 insertions(+), 95 deletions(-) + +commit 2d1d49d32eed60fa7063c34be0cb6cc3ddf14654 +Author: Albert Astals Cid +Date: Sun Oct 20 22:44:26 2024 +0200 + + Remove the path functions from GfxFontLoc + + poppler/GfxFont.cc | 16 +++------------- + poppler/GfxFont.h | 3 --- + poppler/PSOutputDev.cc | 18 +++++++++--------- + poppler/PSOutputDev.h | 8 ++++---- + 4 files changed, 16 insertions(+), 29 deletions(-) + +commit ade5eeef847a75bcb754c8316b6727af15274ff1 +Author: Albert Astals Cid +Date: Sun Oct 20 22:35:59 2024 +0200 + + Return optional string instead of GooString * in some GlobalParam + functions + + poppler/GfxFont.cc | 24 ++++++++++----------- + poppler/GfxFont.h | 8 +++---- + poppler/GlobalParams.cc | 54 + +++++++++++++++++++++------------------------- + poppler/GlobalParams.h | 9 ++++---- + poppler/GlobalParamsWin.cc | 16 +++++++------- + 5 files changed, 52 insertions(+), 59 deletions(-) + +commit 6db287c19fdfc5da8b6f1aba293712442260e3d5 +Author: Albert Astals Cid +Date: Mon Oct 21 19:00:48 2024 +0200 + + Fix installing poppler-config.h + + CMakeLists.txt | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +commit 153c68fe87f1181ce5262a612774260d1d9cb3e0 +Author: Albert Astals Cid +Date: Sun Oct 20 23:27:42 2024 +0200 + + Update (C) + + poppler/Annot.cc | 1 + + poppler/Annot.h | 1 + + 2 files changed, 2 insertions(+) + +commit c6bf2224cac6b9693a390b2655cf9942619919f5 +Author: Carsten Emde +Date: Sat Jul 27 19:29:50 2024 +0000 + + Annots: partial revert of commit 0b474ffb to except form buttons + + The original commit unconditionally creates a new + appearance and thus discards the previous selection + state of a checkbox. As a result, only one checkbox + could be selected at a time. + + Given the motivation for commit 0b474ffb was to affect + free text annotations, we here restore the original + behavior for any formButton. + + Fixes: + Issue #1504 + https://gitlab.gnome.org/GNOME/evince/-/issues/2043 + + poppler/Annot.cc | 19 ++++++++++++++++--- + poppler/Annot.h | 1 + + 2 files changed, 17 insertions(+), 3 deletions(-) + +commit 5b05988b25078b86321a143502c5c196f2ce1079 +Author: Albert Astals Cid +Date: Sun Oct 20 03:18:22 2024 +0200 + + Update (C) + + poppler/CairoFontEngine.cc | 1 + + poppler/CairoOutputDev.cc | 1 + + 2 files changed, 2 insertions(+) + +commit 4a7665d584b69bef03c787a28f5a1b7a6cb22c65 +Author: Albert Astals Cid +Date: Sun Oct 20 03:05:23 2024 +0200 + + Also run all_verify_interface_header_sets over the internal headers + + While at it don't install headers that have no exported classes + and are not included by other installed headers + + .gitlab-ci.yml | 2 +- + CMakeLists.txt | 125 + ++++++++++++++++++------------------------ + fofi/FoFiBase.h | 6 +- + poppler/Array.h | 36 +++++++++++- + poppler/CharCodeToUnicode.h | 3 +- + poppler/Dict.h | 71 +++++++++++++++++++++++- + poppler/Object.h | 125 + ------------------------------------------ + poppler/Sound.h | 7 ++- + poppler/Stream.h | 34 ++++++++++++ + poppler/UnicodeCClassTables.h | 2 + + poppler/UnicodeCompTables.h | 2 + + poppler/UnicodeDecompTables.h | 2 + + poppler/UnicodeMapTables.h | 3 + + poppler/UnicodeTypeTable.cc | 4 +- + poppler/UnicodeTypeTable.h | 5 +- + splash/SplashState.h | 4 +- + 16 files changed, 222 insertions(+), 209 deletions(-) + +commit 38da08bb4df13bdc885b02df1ea4ef30d57d7b22 +Author: Albert Astals Cid +Date: Fri Oct 18 18:47:53 2024 +0200 + + Run all_verify_interface_header_sets to make sure our headers + are usable + + .gitlab-ci.yml | 1 + + CMakeLists.txt | 1 + + cpp/CMakeLists.txt | 16 ++++++++++++++-- + glib/CMakeLists.txt | 17 ++++++++++++++--- + glib/poppler-action.h | 3 +++ + glib/poppler-annot.h | 1 + + glib/poppler-autocleanups.h | 27 --------------------------- + glib/poppler-document.h | 6 ++++++ + glib/poppler-form-field.h | 4 ++++ + glib/poppler-page.h | 11 +++++++++++ + glib/poppler-structure-element.h | 3 +++ + glib/poppler.h | 1 - + qt5/src/CMakeLists.txt | 16 +++++++++++++--- + qt6/src/CMakeLists.txt | 16 +++++++++++++--- + 14 files changed, 84 insertions(+), 39 deletions(-) + +commit 1a3a498acb4e5981ab0a077d3f0fa5f0ca16ea69 +Author: Nelson Benítez León +Date: Sat Sep 28 20:37:29 2024 +0100 + + cairo: prevent infinite loop in functionShadedFill() + + Prevent y_step or x_step to be zero, as that would cause + infinite loop. Reproducible with the pdf file posted in + issue #1520 + + Fixed by setting a minimum step to force upon x_step and + y_step, to avoid approximate or reach infinite loop when + x_step or y_step approximates to or equals zero. + + Thanks Wesley Moore for reporting and debugging. + + Closes #1520 + + poppler/CairoOutputDev.cc | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +commit 14e641421f78f59ac7066741f436403b34a20d12 +Author: Dmitry Shubin +Date: Wed Oct 16 21:17:27 2024 +0000 + + Cairo: Fix selecting fonts in TTC font collections + + poppler/CairoFontEngine.cc | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +commit aab9486215f13ed3da0a75fd6c3ec1334dafd1d9 +Author: Nelson Benítez León +Date: Tue Sep 24 21:19:37 2024 +0100 + + glib: add INSTALL_GLIB_DEMO option to install poppler-glib-demo + + When building poppler-glib-demo (BUILD_GTK_TESTS option) + let's also install it according to a new INSTALL_GLIB_DEMO + option, as otherwise it can only be accessed from the + build directory. + + When using JHBuild to build Poppler, using INSTALL_GLIB_DEMO=ON + will make "jhbuild run poppler-glib-demo some_file.pdf" + to actually work, as the binary gets installed to the + corresponding /bin directory. + + CMakeLists.txt | 1 + + glib/demo/CMakeLists.txt | 3 +++ + 2 files changed, 4 insertions(+) + +commit fd6daae699d15c1f88513f9520b0cb7fd67772c1 +Author: Albert Astals Cid +Date: Mon Oct 14 23:20:52 2024 +0200 + + cmake: Set CMAKE_C_STANDARD to 17 + + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 222594190a0f4022a8e10db5c719d488062d4253 +Author: Albert Astals Cid +Date: Mon Oct 14 23:05:50 2024 +0200 + + qt: Remove cmake definitions that are already in the main cmake + + qt5/src/CMakeLists.txt | 4 ---- + qt6/src/CMakeLists.txt | 4 ---- + 2 files changed, 8 deletions(-) + +commit fe3cb1ea97953ac78ee95a5875e2d512887bbf24 +Author: Albert Astals Cid +Date: Mon Oct 14 22:53:08 2024 +0200 + + poppler 24.10.0 + + CMakeLists.txt | 4 ++-- + NEWS | 20 ++++++++++++++++++++ + cpp/Doxyfile | 2 +- + glib/CMakeLists.txt | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 8 files changed, 28 insertions(+), 8 deletions(-) + +commit c1bd4e66afcbb015e97d915c2fce225fc47155d7 +Author: Sune Vuorela +Date: Tue Oct 1 15:30:42 2024 +0200 + + NSS: Don't try get pubkey manually + + If the nss functions can't find a public key, don't try to dig + into the + nss structures to get one; just leave it empty. + + Fixes: #1522 + + poppler/NSSCryptoSignBackend.cc | 1 - + 1 file changed, 1 deletion(-) + +commit cde04f579055729143ba74c3551127ce2499d8b7 +Author: Albert Astals Cid +Date: Fri Oct 11 18:15:41 2024 +0200 + + Update (C) + + poppler/Link.cc | 1 + + poppler/Link.h | 1 + + 2 files changed, 2 insertions(+) + +commit 73d8e2284049faf29f171f490b1f6f5ec9af6658 +Author: Pratham Gandhi +Date: Fri Oct 11 16:05:55 2024 +0000 + + SubmitForms Link implementation + + poppler/Form.cc | 13 ++++++++- + poppler/Form.h | 4 +++ + poppler/Link.cc | 53 ++++++++++++++++++++++++++++++++++++ + poppler/Link.h | 45 ++++++++++++++++++++++++++++++ + qt5/src/poppler-annotation.cc | 3 ++ + qt5/src/poppler-link-private.h | 12 ++++++++ + qt5/src/poppler-link.cc | 46 +++++++++++++++++++++++++++++++ + qt5/src/poppler-link.h | 60 + ++++++++++++++++++++++++++++++++++++++++ + qt5/src/poppler-page.cc | 23 ++++++++++++++++ + qt6/src/poppler-link-private.h | 12 ++++++++ + qt6/src/poppler-link.cc | 46 +++++++++++++++++++++++++++++++ + qt6/src/poppler-link.h | 62 + ++++++++++++++++++++++++++++++++++++++++++ + qt6/src/poppler-page.cc | 24 ++++++++++++++++ + 13 files changed, 402 insertions(+), 1 deletion(-) + +commit dacf77246ea2d9587c99eb3475db2b8a55af6815 +Author: Pablo Correa Gómez +Date: Wed Oct 9 16:32:38 2024 +0200 + + glib: simplify a bit the code in poppler_print_annot_cb + + So that instead of checking for the typeWidget on every flag, we + return early if the annotation matches, and else arrive at the final + return. + + glib/poppler-page.cc | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit 8407d4e0fc5c2e2a61c395dec0ce1a34a822f1c4 +Author: Pablo Correa Gómez +Date: Wed Oct 9 16:27:14 2024 +0200 + + glib: don't check for annotations print flag in poppler_print_annot_cb + + This is already checked in isVisible inside Annot::draw, and a given + due to us only using poppler_print_annot_cb when printing option is + true + + glib/poppler-page.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 5fc76fe792e6aea291ad7f4af8b4787d4b956d02 +Author: Pablo Correa Gómez +Date: Wed Oct 9 16:15:37 2024 +0200 + + glib: don't check for hidden annotations in poppler_print_annot_cb + + This is already checked in isVisible inside Annot::draw, and a given + due to us only using poppler_print_annot_cb when printing option is + true + + glib/poppler-page.cc | 4 ---- + 1 file changed, 4 deletions(-) + +commit 07b45c0c4330898d10a9d5b60842bf0168a6bb88 +Author: Pablo Correa Gómez +Date: Wed Oct 9 15:13:15 2024 +0200 + + glib: fix closure GI annotation + + Fixes the following warning: + + ../../glib/poppler-form-field.cc:590: Warning: Poppler: invalid + "closure" annotation: only valid on callback parameters + + glib/poppler-form-field.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit e2889c84adfc7e8903ab12eeed248c71a0ccd729 +Author: Hubert Figuière +Date: Tue Oct 8 18:45:39 2024 -0400 + + poppler: fuzz: init page list in getNumPage() + + This fixes a use-after-free on malformed documents that call + getNumPage before others. + + This regression was introduced by !1556 + + Close https://gitlab.freedesktop.org/poppler/poppler/-/issues/1518 + Close https://gitlab.freedesktop.org/poppler/poppler/-/issues/1523 + + Signed-off-by: Hubert Figuière + + poppler/Catalog.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 28367297301753d4653f0c1f9ae25583f3f22453 +Author: Hubert Figuiere +Date: Wed Oct 9 20:52:40 2024 +0000 + + glib: test: Properly cleanup in check_bb.c + + glib/tests/check_bb.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit dc4a00f6d531d8e8072ae8d71bf77639bcaad099 +Author: Pablo Correa Gómez +Date: Wed Aug 28 12:09:03 2024 +0200 + + glib: add auto_cleanup functions so that consumers can use g_auto* + + This is of great help as consumers of the GLib API. This way we can + define things like + + g_autoptr (PopplerSignatureInfo) info = NULL; + + And it will get auto-freed at the end of the current scope + + glib/CMakeLists.txt | 1 + + glib/poppler-action.h | 4 ++++ + glib/poppler-autocleanups.h | 27 +++++++++++++++++++++++++++ + glib/poppler-document.cc | 35 + +++++++++++++++++++++++++++++++++-- + glib/poppler-document.h | 4 ---- + glib/poppler-form-field.cc | 11 +++++++++-- + glib/poppler-page.h | 20 ++++++++++++++++++++ + glib/poppler-structure-element.cc | 10 ++++++++++ + glib/poppler.h | 1 + + 9 files changed, 105 insertions(+), 8 deletions(-) + +commit 2b251ff4b8a71ed94ec2488c296adddc76a97801 +Author: Volker Krause +Date: Fri Oct 4 18:28:44 2024 +0200 + + Remove Qt 5 Android CI + + This is based on images by KDE that are about to be removed, as + part of + retiring Qt 5 Android CI infrastructure there. + + Practically this isn't much of a problem, as Qt 5 is meanwhile lagging + behind several Android SDK versions and thus isn't able to produce + APKs + that are compliant with the Google Play requirements anymore. + + .gitlab-ci.yml | 53 ----------------------------------------------------- + 1 file changed, 53 deletions(-) + +commit 1bce1c823a08c9bd2b63a186339601188f80c08d +Author: Albert Astals Cid +Date: Fri Oct 4 08:01:29 2024 +0200 + + Update (C) + + poppler/Page.cc | 1 + + poppler/Page.h | 1 + + qt6/src/poppler-annotation.cc | 1 + + 3 files changed, 3 insertions(+) + +commit bd040bd1956a701b74b203257d59b86bdf743da5 +Author: Pablo Correa Gómez +Date: Thu Oct 3 14:39:58 2024 +0200 + + page: remove unused printing option from createGfx + + glib/poppler-page.cc | 6 ++---- + poppler/Page.cc | 4 ++-- + poppler/Page.h | 3 +-- + 3 files changed, 5 insertions(+), 8 deletions(-) + +commit 9e34c5d506bbb6155d74469b4dd3242ff5a0a2ab +Author: Sune Vuorela +Date: Thu Oct 3 14:58:21 2024 +0200 + + Fix text encoding for new signature workflow + + The strings to be used are unicode GooString, not utf-8 / ascii + std::strings, so fix it. + + Else plain English strings ends up embedding 16-40 mb of fonts for the + cjk and other characters. + + qt6/src/poppler-annotation.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 75a49f9d839fa34d713de8d863fdefd67fcfa85d +Author: Albert Astals Cid +Date: Fri Oct 4 06:57:26 2024 +0200 + + CI: Fix Android Qt6 build + + .gitlab-ci.yml | 19 +++++++++++++++++-- + CMakeLists.txt | 2 +- + poppler/Stream.cc | 10 +++++----- + 3 files changed, 23 insertions(+), 8 deletions(-) + +commit 24b6ef4559eb38c41e60816fb1398acb46fd6e27 +Author: Nicolas Fella +Date: Wed May 8 13:45:08 2024 +0200 + + Add API for creating signature field without signing immediately + + Currently Poppler allows to create a signature and sign it in one step + as well as singing an unsinged field. However there is no way to + create an unsigned field and sign it later. + + This adds a SignatureAnnotation class that can be added like any + annotation and signed later + + poppler/PDFDoc.cc | 57 ++++-- + poppler/PDFDoc.h | 12 ++ + qt6/src/CMakeLists.txt | 1 + + qt6/src/poppler-annotation.cc | 203 +++++++++++++++++- + qt6/src/poppler-annotation.h | 88 +++++++- + qt6/src/poppler-converter.h | 465 + ++++++++++++++++++++++++++++++++++++++++++ + qt6/src/poppler-qt6.h | 403 +----------------------------------- + 7 files changed, 809 insertions(+), 420 deletions(-) + +commit c590d4ed1620abb329aba0bd5e3fc07668788134 +Author: Albert Astals Cid +Date: Sat Sep 7 17:32:06 2024 +0200 + + Update (C) + + poppler/Gfx.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 4113690bd8b756be9e35c03d552936f404313764 +Author: Albert Astals Cid +Date: Sat Jun 15 01:54:41 2024 +0200 + + Make GfxShading::parse & friends use unique_ptr + + poppler/Gfx.cc | 22 +++--- + poppler/Gfx.h | 2 +- + poppler/GfxState.cc | 189 + ++++++++++++++++++++++------------------------------ + poppler/GfxState.h | 30 ++++----- + 4 files changed, 106 insertions(+), 137 deletions(-) + +commit 96078968bd7f4d2c6dae9d5838ca65412520948f +Author: Albert Astals Cid +Date: Fri Jun 14 17:59:30 2024 +0200 + + Make GfxPattern::parse and friends return unique_ptr + + poppler/Gfx.cc | 12 ++++++------ + poppler/Gfx.h | 2 +- + poppler/GfxState.cc | 56 + ++++++++++++++++++++++++----------------------------- + poppler/GfxState.h | 24 +++++++++++------------ + 4 files changed, 44 insertions(+), 50 deletions(-) + +commit 63a277efb5bc3cfadfe86bee384b0e997ce3e9dc +Author: Albert Astals Cid +Date: Fri Jun 14 17:46:07 2024 +0200 + + Make GfxColorSpace::parse and friends return unique_ptr + + poppler/CairoOutputDev.cc | 6 +- + poppler/Gfx.cc | 103 ++++++------ + poppler/GfxState.cc | 389 + +++++++++++++++++++-------------------------- + poppler/GfxState.h | 140 ++++++++-------- + poppler/OutputDev.h | 14 +- + poppler/Page.cc | 7 +- + poppler/PopplerCache.h | 12 +- + poppler/SplashOutputDev.cc | 2 +- + poppler/TextOutputDev.cc | 2 +- + splash/Splash.cc | 2 +- + splash/SplashBitmap.cc | 19 +-- + splash/SplashBitmap.h | 11 +- + 12 files changed, 319 insertions(+), 388 deletions(-) + +commit 553ce76c27563a2dfacc34fe79f0b2ba365787fa +Author: Albert Astals Cid +Date: Fri Sep 6 14:26:19 2024 +0200 + + CI: newer gnupg/gpgme so it builds on newer debian unstable + + do-the-gnupg-2.4-dance.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit dce1f7850f8a735612c0c864922310e2ee8f7446 +Author: Albert Astals Cid +Date: Wed Sep 4 23:54:16 2024 +0200 + + Update (C) + + poppler/CairoOutputDev.cc | 1 + + poppler/Gfx.cc | 1 + + poppler/GfxState.cc | 1 + + poppler/GfxState.h | 1 + + 4 files changed, 4 insertions(+) + +commit 376e37a990a413a70a81c68c3cfe3bb77faf304d +Author: Athul Raj Kollareth +Date: Wed Sep 4 21:29:47 2024 +0000 + + Replace some ints with Enums + + poppler/CairoOutputDev.cc | 12 ++++++------ + poppler/Gfx.cc | 28 ++++++++++++++-------------- + poppler/GfxState.cc | 6 +++--- + poppler/GfxState.h | 47 + ++++++++++++++++++++++++++++++++--------------- + 4 files changed, 55 insertions(+), 38 deletions(-) + +commit 0553771fbe02b1d6d3ae5ddab697c2f8629aedec +Author: Albert Astals Cid +Date: Wed Jul 3 14:06:32 2024 +0200 + + Use the boosts own cmake file as suggested by cmake itself + + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit e23bd900b7c5f3262c3b6c5fb20d7569ca5193db +Author: Albert Astals Cid +Date: Sun Sep 1 23:49:58 2024 +0200 + + poppler 24.09.0 + + CMakeLists.txt | 4 ++-- + NEWS | 8 ++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 13 insertions(+), 5 deletions(-) + +commit 095789955b23bf271cc5ecb3a654c9c2df297100 +Author: Albert Astals Cid +Date: Wed Aug 28 19:32:56 2024 +0200 + + Update (C) + + poppler/CairoFontEngine.cc | 1 + + poppler/CairoOutputDev.cc | 1 + + poppler/Error.cc | 1 + + poppler/Form.cc | 1 + + poppler/GfxFont.cc | 1 + + poppler/PDFDoc.cc | 1 + + poppler/XRef.cc | 1 + + test/perf-test.cc | 1 + + 8 files changed, 8 insertions(+) + +commit fd057184e6f85ca920df5285fabb30ec5b634ea2 +Author: Vincent Lefevre +Date: Wed Aug 28 11:41:17 2024 +0200 + + Remove incorrect \n and \r characters at the end of error() strings + + Some error() message strings end with \n, and \r\n for one of them. + This is incorrect according to the error() code (in poppler/Error.cc), + where control characters are changed to a printable form; a \n is + added automatically by this function. + + This affects at least pdfinfo and pdftotext (and the xpdf PDF viewer), + which output error messages like + + Syntax Error: Can't get Fields array<0a> + + with a spurious <0a> at the end. + + This patch removes these \n and \r; it has been obtained with + + perl -pi -e 's/(error\(.*?)(\\[nr])+/\1/' $(git grep -l + 'error(.*\\n') + + Signed-off-by: Vincent Lefevre + + poppler/CairoFontEngine.cc | 8 ++++---- + poppler/CairoOutputDev.cc | 6 +++--- + poppler/Form.cc | 32 ++++++++++++++++---------------- + poppler/GfxFont.cc | 2 +- + poppler/PDFDoc.cc | 2 +- + poppler/XRef.cc | 12 ++++++------ + test/perf-test.cc | 2 +- + 7 files changed, 32 insertions(+), 32 deletions(-) + +commit 0ae1c079864b332de5f6050172ded2e8a1ac1071 +Author: Albert Astals Cid +Date: Wed Aug 28 09:00:21 2024 +0200 + + Update (C) + + poppler/Catalog.h | 1 + + poppler/SplashOutputDev.cc | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit b8bb2e46ff289c25f097c2f6baeb21642749f761 +Author: Hubert Figuière +Date: Sun May 26 08:57:18 2024 -0400 + + poppler/Catalog: map page by Ref to make findPage() faster (#1485) + + findPage used to iterate linearly to find a page by Ref, instead + we use a map. + + See https://gitlab.freedesktop.org/poppler/poppler/-/issues/1485 + + Signed-off-by: Hubert Figuière + + poppler/Catalog.cc | 232 + +++++++++++++++++++++++++++++++---------------------- + poppler/Catalog.h | 5 ++ + 2 files changed, 142 insertions(+), 95 deletions(-) + +commit 5a25ac7692d76f3c3e5e5850a062209f3482d85c +Author: Albert Astals Cid +Date: Fri Aug 16 00:19:14 2024 +0200 + + Add support for Catalog OpenAction + + poppler/Catalog.cc | 18 ++++++++++++++++++ + poppler/Catalog.h | 4 +++- + poppler/JSInfo.cc | 3 ++- + 3 files changed, 23 insertions(+), 2 deletions(-) + +commit f8bcc96311c5fac72dbeda48fcf9f53b36e51d8c +Author: Albert Astals Cid +Date: Thu Aug 22 09:22:26 2024 +0200 + + Fix crash when softMask fails to allocate + + poppler/SplashOutputDev.cc | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +commit ff18a6f3d9dc8e881e881f2be271de9df60c294d +Author: Albert Astals Cid +Date: Mon Aug 5 00:19:14 2024 +0200 + + Update (C) + + poppler/CairoFontEngine.h | 2 +- + test/cairo-thread-test.cc | 2 +- + utils/pdftocairo.cc | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit 0a902dce12f1e6b2e135775a4fb4cb6e2dd0c61c +Author: Adrian Johnson +Date: Thu Jul 25 21:44:37 2024 +0930 + + Fix cairo_font_face_t leak in CairoFontEngine + + Remove the external font cache from CairoFontEngine. Fixing the leak + would require keeping track of which document is using each external + font so fonts can be removed from the cache when the document is + closed. As fonts are already cached in fontCache, and the external + font caching did not exist prior to the the CairoFontEngine refactor, + the marginal benefit of sharing external fonts between documents is + not worth the effort. + + poppler/CairoFontEngine.cc | 54 + +++++----------------------------------------- + poppler/CairoFontEngine.h | 8 +------ + test/cairo-thread-test.cc | 7 ++++++ + utils/pdftocairo.cc | 7 ++++++ + 4 files changed, 20 insertions(+), 56 deletions(-) + +commit b58a49e4e576492219b87ed09ed8e02d565d202c +Author: Albert Astals Cid +Date: Fri Aug 2 00:59:03 2024 +0200 + + poppler 24.08.0 + + CMakeLists.txt | 4 ++-- + NEWS | 16 ++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 23 insertions(+), 7 deletions(-) + +commit cf7b778c261ec96d3adbba0db3f46a5e8135a829 +Author: Albert Astals Cid +Date: Fri Aug 2 00:53:42 2024 +0200 + + Update (C) + + poppler/Annot.cc | 1 + + poppler/Form.cc | 1 + + poppler/Form.h | 1 + + poppler/GlobalParamsWin.cc | 2 +- + qt5/src/poppler-form.cc | 1 + + qt5/src/poppler-form.h | 1 + + qt6/src/poppler-form.cc | 1 + + qt6/src/poppler-form.h | 1 + + 8 files changed, 8 insertions(+), 1 deletion(-) + +commit ce0d46ec9c0f10cf704dfd6825942b3f7d549f75 +Author: Albert Astals Cid +Date: Tue Jul 30 00:51:25 2024 +0200 + + qt6: Port test to use non deprecated API + + qt6/tests/poppler-forms.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +commit 415365ffef95251417e288e01d3d12477fb89c6a +Author: Pratham Gandhi +Date: Tue Jul 16 01:23:58 2024 +0530 + + Implemented support for modifying the appearance stream text in form + field choice + + poppler/Annot.cc | 2 +- + poppler/Form.cc | 23 +++++++++++++++++++++++ + poppler/Form.h | 6 +++++- + qt5/src/poppler-form.cc | 8 ++++++++ + qt5/src/poppler-form.h | 8 ++++++++ + qt6/src/poppler-form.cc | 8 ++++++++ + qt6/src/poppler-form.h | 8 ++++++++ + 7 files changed, 61 insertions(+), 2 deletions(-) + +commit a3a3fe14939c72ea304c9f1192a38e1ff26e51e4 +Author: Adrian Johnson +Date: Sun Jul 21 20:34:31 2024 +0930 + + Fix buffer overflow in GlobalParamsWin + + poppler/GlobalParamsWin.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit a84c6ecd633a4dad6b4bfb275fb339136ec19341 +Author: Albert Astals Cid +Date: Mon Jul 15 15:35:26 2024 +0200 + + pdftops: Fix crash on files with font without name and using -passfont + + poppler/GfxFont.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 6bb720f9ea629f03f07a6f06e57339a3c71e56f2 +Author: Albert Astals Cid +Date: Mon Jul 8 23:26:47 2024 +0200 + + pdfinfo: Don't crash on malformed files + + utils/pdfinfo.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 35b16a51af89f8debb71cc880f6203da2e2db4f8 +Author: Albert Astals Cid +Date: Sat Jul 6 12:21:41 2024 +0200 + + Update (C) + + poppler/Catalog.cc | 1 + + poppler/Catalog.h | 1 + + 2 files changed, 2 insertions(+) + +commit b3edc1d9bc824ae4cf3f0dd58d22671bb9d8b525 +Author: Sune Vuorela +Date: Sat Jul 6 10:20:03 2024 +0000 + + Simplify memory handling in Catalog NameTree + + poppler/Catalog.cc | 70 + +++++++---------------------------------------- + poppler/Catalog.h | 10 ++----- + qt6/tests/check_links.cpp | 4 +++ + 3 files changed, 16 insertions(+), 68 deletions(-) + +commit 01a88235ea7b66db19ef1a3fce9a84d7e9850737 +Author: Albert Astals Cid +Date: Mon Jul 1 23:22:39 2024 +0200 + + poppler 24.07.0 + + CMakeLists.txt | 4 ++-- + NEWS | 19 +++++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 26 insertions(+), 7 deletions(-) + +commit fb8e6cfdab035fcf0e8919f8700ff2c86c627177 +Author: Albert Astals Cid +Date: Mon Jul 1 22:29:59 2024 +0200 + + Add missing include + + fofi/FoFiTrueType.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit a16aa8bc839e5dd4a2c8d75aff010416477b392b +Author: Albert Astals Cid +Date: Mon Jul 1 21:22:52 2024 +0200 + + Update (C) + + qt5/src/poppler-annotation.cc | 1 + + qt5/src/poppler-document.cc | 1 + + qt5/src/poppler-link-private.h | 1 + + qt5/src/poppler-link.cc | 1 + + qt5/src/poppler-link.h | 1 + + qt5/src/poppler-page.cc | 1 + + qt5/src/poppler-qt5.h | 1 + + qt6/src/poppler-document.cc | 1 + + qt6/src/poppler-link-private.h | 1 + + qt6/src/poppler-link.cc | 1 + + qt6/src/poppler-link.h | 1 + + qt6/src/poppler-page.cc | 1 + + qt6/src/poppler-qt6.h | 1 + + 13 files changed, 13 insertions(+) + +commit 81b83e83c053420d452a31d0cc6321965cb5e358 +Author: Pratham Gandhi +Date: Mon Jul 1 19:19:21 2024 +0000 + + Qt: Implement reset forms link + + qt5/src/poppler-annotation.cc | 4 ++++ + qt5/src/poppler-document.cc | 19 +++++++++++++++++++ + qt5/src/poppler-link-private.h | 10 ++++++++++ + qt5/src/poppler-link.cc | 12 ++++++++++++ + qt5/src/poppler-link.h | 28 ++++++++++++++++++++++++++++ + qt5/src/poppler-page.cc | 13 ++++++++++--- + qt5/src/poppler-qt5.h | 7 +++++++ + qt6/src/poppler-document.cc | 19 +++++++++++++++++++ + qt6/src/poppler-link-private.h | 10 ++++++++++ + qt6/src/poppler-link.cc | 12 ++++++++++++ + qt6/src/poppler-link.h | 28 ++++++++++++++++++++++++++++ + qt6/src/poppler-page.cc | 13 ++++++++++--- + qt6/src/poppler-qt6.h | 7 +++++++ + 13 files changed, 176 insertions(+), 6 deletions(-) + +commit 5e4d80275ee7800ec266f58d5178b34654b5bf12 +Author: Albert Astals Cid +Date: Thu Jun 27 19:32:18 2024 +0200 + + Update (C) + + fofi/FoFiType1.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit fe0513a2bd4fe7a0ba9c3caf73573098392d2267 +Author: Albert Astals Cid +Date: Wed Jun 26 00:20:16 2024 +0200 + + Fix crash in FoFiType1::parse + + When the content to parse ends in "/FontName " + + fofi/FoFiType1.cc | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +commit cd59895eb14d98776a416a9aa8d7a3cdc788b278 +Author: Albert Astals Cid +Date: Fri Jun 21 00:43:40 2024 +0200 + + Update (C) + + poppler/UTF.cc | 2 +- + poppler/UTF.h | 2 +- + utils/Win32Console.cc | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit 935734b8a272ad8f1acdd9df057b8e3616c181c5 +Author: Nelson Benítez León +Date: Thu Jun 20 09:00:47 2024 +0000 + + glib find text fuzzer: correctly free the matches list + + glib/tests/fuzzing/find_text_fuzzer.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 3a9a34a4bee771c547ceaeeab2cd639d825fa5b5 +Author: Adrian Johnson +Date: Sat Jun 15 19:22:25 2024 +0930 + + Reorder utf8ToUtf16() args so each length argument follows the string + + poppler/UTF.cc | 12 ++---------- + poppler/UTF.h | 14 +++++++------- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + utils/Win32Console.cc | 2 +- + 5 files changed, 12 insertions(+), 20 deletions(-) + +commit c629b189898694e3bd27039d834ec4423fbf18e8 +Author: Adrian Johnson +Date: Sat Jun 15 14:04:48 2024 +0930 + + Reorder utf16ToUtf8() args so each length argument follows the string + + poppler/UTF.cc | 12 ++---------- + poppler/UTF.h | 15 ++++++++------- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + 4 files changed, 12 insertions(+), 19 deletions(-) + +commit 4551469474b665dbd3159b526b3998a9acf08542 +Author: Albert Astals Cid +Date: Sat Jun 15 12:01:25 2024 +0200 + + glib: fuzz: Free memory + + glib/tests/fuzzing/find_text_fuzzer.cc | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +commit ef439ea30f92bc1cf9dd584e9f7b514e6ec7a240 +Author: Albert Astals Cid +Date: Mon Jun 10 11:06:34 2024 +0200 + + Make MediaRendition::copy return a unique_ptr + + poppler/Rendition.cc | 6 +++--- + poppler/Rendition.h | 4 ++-- + qt5/src/poppler-link.cc | 24 +++++++++++++----------- + qt5/src/poppler-link.h | 17 +++++++++++++++-- + qt5/src/poppler-media.cc | 12 +++++++----- + qt5/src/poppler-media.h | 12 ++++++++++-- + qt5/src/poppler-page.cc | 4 ++-- + qt6/src/poppler-link.cc | 24 +++++++++++++----------- + qt6/src/poppler-link.h | 17 +++++++++++++++-- + qt6/src/poppler-media.cc | 12 +++++++----- + qt6/src/poppler-media.h | 10 ++++++++-- + qt6/src/poppler-page.cc | 4 ++-- + 12 files changed, 97 insertions(+), 49 deletions(-) + +commit e6f279ca1db6f6364dd44d46a6d3edf02c98f5b5 +Author: Albert Astals Cid +Date: Fri Jun 14 10:17:11 2024 +0200 + + We have maxUtf8 so pass it along to be safer + + poppler/UTF.cc | 4 ++-- + poppler/UTF.h | 2 +- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + 4 files changed, 5 insertions(+), 5 deletions(-) + +commit 8465b523ad9a67fa51d923da7bda7d0209067acb +Author: Albert Astals Cid +Date: Fri Jun 14 10:14:50 2024 +0200 + + Fix crash when converting invalid utf16 to utf8 + + poppler/UTF.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2c88fdeff5dbbe19a357b604fbd269b31e337088 +Author: Albert Astals Cid +Date: Thu Jun 13 21:39:03 2024 +0200 + + Prevent some more overflows in broken files + + poppler/Stream.h | 11 +++++++++-- + splash/Splash.cc | 18 +++++++++++++----- + 2 files changed, 22 insertions(+), 7 deletions(-) + +commit 5399e7695f725744290cfd3e68bc0ca1109638d5 +Author: Albert Astals Cid +Date: Thu Jun 13 14:46:24 2024 +0000 + + glib: fuzz: poppler_page_find_text requirement is that the text + is utf8 + + glib/tests/fuzzing/find_text_fuzzer.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 83c22b3a66a188dd9a17a013aa7a9c38e3e87f89 +Author: Albert Astals Cid +Date: Thu Jun 13 16:26:37 2024 +0200 + + glib: fuzz: free doc properly + + glib/tests/fuzzing/doc_attr_fuzzer.cc | 1 + + glib/tests/fuzzing/find_text_fuzzer.cc | 2 ++ + glib/tests/fuzzing/label_fuzzer.cc | 2 ++ + 3 files changed, 5 insertions(+) + +commit 274e054591aac4dc45e21bb8b194fd40673ab6b7 +Author: Albert Astals Cid +Date: Thu Jun 13 16:15:23 2024 +0200 + + Also initialize rootGen + + poppler/XRef.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 1f657b4b5ad392762ec7685198f117f682044f53 +Author: Albert Astals Cid +Date: Thu Jun 13 16:04:07 2024 +0200 + + glib-mkenums is required + + So fail on cmake stage rather than on compile stage + + glib/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 3f7ed54dc7572504c8148a2cc1d2a13b0298b5df +Author: Albert Astals Cid +Date: Wed Jun 12 23:15:17 2024 +0200 + + Forgot to increase the SOVERSION for 24.06.0 + + (cherry picked from commit 10914f82cee06ea95ace19c5d74a2c2ff2bab4dc) + + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 753217915013a6e0e19e83872082cda1193e5f50 +Author: Albert Astals Cid +Date: Wed Jun 12 23:02:16 2024 +0200 + + Update (C) + + fofi/FoFiTrueType.cc | 1 + + fofi/FoFiTrueType.h | 1 + + 2 files changed, 2 insertions(+) + +commit b16c379582ef1af6101aaa20e69a9abfd32fad7d +Author: Sune Vuorela +Date: Wed Jun 12 14:06:29 2024 +0200 + + FoFiTrueType: less manual memory handling + + - use vector instead of pointer,length for owning structures + - use span instead of pointer,length for non-owning arguments + - use std::array for most fixed size arrays + - remove gmem usage + + fofi/FoFiTrueType.cc | 184 + +++++++++++++++++++++------------------------------ + fofi/FoFiTrueType.h | 12 ++-- + 2 files changed, 82 insertions(+), 114 deletions(-) + +commit 77457e1350720d197d1b2e879aa9bf11a0d575d2 +Author: Albert Astals Cid +Date: Tue Jun 11 22:09:31 2024 +0200 + + Revert "Remove unused GfxImageColorMap::copy" + + kitinerary uses it + + This reverts commit 912b1d5c583677e2629a0a2c4623baa324adb62b. + + poppler/GfxState.cc | 54 + ++++++++++++++++++++++++++++++++++++++++++++++++++++- + poppler/GfxState.h | 7 ++++++- + 2 files changed, 59 insertions(+), 2 deletions(-) + +commit 0554731052d1a97745cb179ab0d45620589dd9c4 +Author: Albert Astals Cid +Date: Fri Jun 7 00:54:55 2024 +0200 + + pdfinfo: Fix crash in broken documents when using -dests + + utils/pdfinfo.cc | 35 +++++++++++++++-------------------- + 1 file changed, 15 insertions(+), 20 deletions(-) + +commit 912b1d5c583677e2629a0a2c4623baa324adb62b +Author: Albert Astals Cid +Date: Mon Jun 10 11:16:29 2024 +0200 + + Remove unused GfxImageColorMap::copy + + poppler/GfxState.cc | 54 + +---------------------------------------------------- + poppler/GfxState.h | 7 +------ + 2 files changed, 2 insertions(+), 59 deletions(-) + +commit 37fda1d8b7e9d987fc5fa9ca5494c5c796ef4fed +Author: Pratham Gandhi +Date: Thu Jun 6 18:19:02 2024 +0530 + + Handled the additional document actions in qt frontends + + qt5/src/poppler-document.cc | 31 +++++++++++++++++++++++++++++++ + qt5/src/poppler-qt5.h | 25 +++++++++++++++++++++++++ + qt6/src/poppler-document.cc | 30 ++++++++++++++++++++++++++++++ + qt6/src/poppler-qt6.h | 25 +++++++++++++++++++++++++ + 4 files changed, 111 insertions(+) + +commit 1e898d55e66b4c39b2349e17756969fca7762e9f +Author: Albert Astals Cid +Date: Thu Jun 6 22:55:22 2024 +0200 + + Update (C) + + poppler/GfxFont.cc | 1 + + poppler/GfxFont.h | 1 + + 2 files changed, 2 insertions(+) + +commit 938af005733a77b13cee2086469ad9eed8e1997c +Author: Sune Vuorela +Date: Thu Jun 6 20:54:19 2024 +0000 + + GfxFonts: Switch font widths from pointer, length to std::vector + + poppler/GfxFont.cc | 65 + ++++++++++++------------------------------------------ + poppler/GfxFont.h | 7 ++---- + 2 files changed, 16 insertions(+), 56 deletions(-) + +commit 64828896a3bff4adafd2f3cac766165bb09a32a1 +Author: Albert Astals Cid +Date: Sun Jun 2 21:05:18 2024 +0200 + + poppler 24.06.0 + + CMakeLists.txt | 2 +- + NEWS | 15 +++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 19 insertions(+), 4 deletions(-) + +commit 258f3eec8ef87be9d54847f697e2fbabc7726bf9 +Author: Pablo Correa Gómez +Date: Mon Jan 22 20:39:07 2024 +0100 + + glib: properly document return value from poppler_font_info_scan + + glib/demo/fonts.c | 3 ++- + glib/poppler-document.cc | 7 +++++-- + 2 files changed, 7 insertions(+), 3 deletions(-) + +commit 71d6b59a705de051feffd2237f55820302322423 +Author: Albert Astals Cid +Date: Wed May 29 19:39:28 2024 +0200 + + Update (C) + + poppler/CurlCachedFile.cc | 1 + + poppler/Error.cc | 1 + + poppler/PSOutputDev.h | 2 +- + utils/pdfsig.cc | 2 +- + 4 files changed, 4 insertions(+), 2 deletions(-) + +commit 3a1d6c0e10875c9899911d0f6661c87d6f1a3dc2 +Author: Oliver Sander +Date: Mon May 13 09:42:25 2024 +0200 + + Let GooString::format return a std::string object + + ... rathern than std::unique_ptr. In many cases this + avoids + a small heap allocation. + + fofi/FoFiTrueType.cc | 66 +++++++-------- + fofi/FoFiType1C.cc | 180 + ++++++++++++++++++++-------------------- + glib/poppler-document.cc | 4 +- + goo/GooString.cc | 8 +- + goo/GooString.h | 2 +- + poppler/Annot.cc | 8 +- + poppler/CurlCachedFile.cc | 4 +- + poppler/PSOutputDev.cc | 38 ++++----- + qt5/tests/check_annotations.cpp | 4 +- + qt5/tests/check_goostring.cpp | 57 ++++++------- + qt6/tests/check_annotations.cpp | 4 +- + qt6/tests/check_goostring.cpp | 57 ++++++------- + utils/HtmlOutputDev.cc | 50 +++++------ + utils/HtmlOutputDev.h | 4 +- + utils/pdfsig.cc | 10 +-- + utils/pdftohtml.cc | 8 +- + 16 files changed, 253 insertions(+), 251 deletions(-) + +commit 6da9eeac408d8daa6b882797bd20b32b284e95bb +Author: Oliver Sander +Date: Tue May 7 22:02:19 2024 +0200 + + writePSTextLine: Take a std::string argument + + poppler/PSOutputDev.cc | 13 +++++++------ + poppler/PSOutputDev.h | 2 +- + 2 files changed, 8 insertions(+), 7 deletions(-) + +commit 1aa70f95fae452499a7db403030ace0e819eba1e +Author: Oliver Sander +Date: Tue May 7 17:52:51 2024 +0200 + + HtmlImage: Store filename as std::string + + Rather than as std::unique_ptr. This saves + one pointer indirection. + + utils/HtmlOutputDev.cc | 14 +++++++------- + utils/HtmlOutputDev.h | 2 +- + 2 files changed, 8 insertions(+), 8 deletions(-) + +commit 9bf935cde5ece00c54e397620244b77303d63c11 +Author: Oliver Sander +Date: Wed Apr 17 10:58:14 2024 +0200 + + Make GooString::formatv return std::string by value + + Rather than returning a std::unique_ptr. + This avoids one heap allocation for the result object. + + goo/GooString.cc | 8 ++++---- + goo/GooString.h | 2 +- + poppler/Error.cc | 5 ++--- + poppler/PSOutputDev.cc | 4 ++-- + 4 files changed, 9 insertions(+), 10 deletions(-) + +commit 2fb40d2db82e0650500d76b61a0fae47b49d4193 +Author: Albert Astals Cid +Date: Wed May 29 19:19:28 2024 +0200 + + Update (C) + + poppler/CairoOutputDev.cc | 2 +- + poppler/CairoOutputDev.h | 2 +- + poppler/Catalog.cc | 1 + + poppler/Stream.h | 1 + + 4 files changed, 4 insertions(+), 2 deletions(-) + +commit a63248281d5162047e8640c3275f65fc4c1e8e6e +Author: Fernando Herrera +Date: Wed May 29 17:18:31 2024 +0000 + + Fix BaseMemStream::getPos casting + + poppler/Stream.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit b6edd41c86d391dea3bd866b4ef65deb41405937 +Author: Hubert Figuiere +Date: Sun May 26 08:53:14 2024 +0000 + + poppler/Catalog: call getNumPages() only once in findPage() + + poppler/Catalog.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 9a8599e938565cb9c1417aba718aaa5f5aa441d9 +Author: Adrian Johnson +Date: Sat May 25 13:49:38 2024 +0930 + + Remove all cairo include guards for cairo < 1.16 + + poppler/CairoOutputDev.cc | 347 + +--------------------------------------------- + poppler/CairoOutputDev.h | 18 --- + 2 files changed, 4 insertions(+), 361 deletions(-) + +commit eca654a789cdd1ddbafbf17d49fd1ca1670f407f +Author: Carl Schwan +Date: Wed May 15 00:42:01 2024 +0200 + + Update Qt6 doc example + + Poppler::Document and Poppler::Page are now wrapped inside unique + pointers. + + qt6/src/Mainpage.dox | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +commit 50659bc5242852e6828a017eadc5ff374b9b6524 +Author: Albert Astals Cid +Date: Tue May 14 00:06:29 2024 +0200 + + Use the non deprecated version of QString::fromUcs4 + + qt6/tests/check_utf8document.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 6522a8c8ae80941a5483d70e985939cb0678c141 +Author: Albert Astals Cid +Date: Fri May 3 00:00:19 2024 +0200 + + Update (C) + + poppler/CairoFontEngine.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit fceaba5461e276121e50d858169a53f7434b1e05 +Author: Albert Astals Cid +Date: Thu May 2 23:58:38 2024 +0200 + + Fix MSVC build + + poppler/PDFDoc.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 574dccdf745232aceea1d569461d7e5c217796a9 +Author: Sune Vuorela +Date: Thu May 2 16:16:05 2024 +0200 + + Fix msvc build + + Apparantly, msvc can't handle defaulted constructors where the class + contains a incomplete type in a unique_ptr. + + Move the defaulted constructor implementation to cpp file to ensure it + is no longer a incomplete type + + poppler/SignatureInfo.cc | 1 + + poppler/SignatureInfo.h | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 9e4d8d56f73989efb11925588592797f1a8db1f7 +Author: Adrian Johnson +Date: Thu May 2 16:24:24 2024 +0930 + + Adding missing restoreState() + + poppler/CairoFontEngine.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 16ff06df9e793d85162096f59d9f9545e07d4aa4 +Author: Albert Astals Cid +Date: Thu May 2 00:29:12 2024 +0200 + + Update (C) + + poppler/PDFDoc.cc | 2 +- + poppler/Stream.cc | 1 + + poppler/Stream.h | 1 + + qt5/src/poppler-qiodeviceoutstream-private.h | 1 + + qt5/src/poppler-qiodeviceoutstream.cc | 1 + + qt6/src/poppler-qiodeviceoutstream-private.h | 1 + + qt6/src/poppler-qiodeviceoutstream.cc | 1 + + 7 files changed, 7 insertions(+), 1 deletion(-) + +commit f265e5aba50b6811471bd7e3a07944dfeeb85186 +Author: Sune Vuorela +Date: Mon Apr 22 14:14:30 2024 +0200 + + copy data streams in 4096 chunks rather than a byte at time + + poppler/PDFDoc.cc | 24 + +++++++++++++++--------- + poppler/Stream.cc | 5 +++++ + poppler/Stream.h | 5 +++++ + qt5/src/poppler-qiodeviceoutstream-private.h | 1 + + qt5/src/poppler-qiodeviceoutstream.cc | 5 +++++ + qt6/src/poppler-qiodeviceoutstream-private.h | 1 + + qt6/src/poppler-qiodeviceoutstream.cc | 5 +++++ + 7 files changed, 37 insertions(+), 9 deletions(-) + +commit 092a012fe17196b35e640a7ca3802538c75fbf78 +Author: Sune Vuorela +Date: Mon Apr 22 14:15:45 2024 +0200 + + Use std::span for some data, size combos + + With c++20, we have std::span which is a nice wrapper around a pointer + and a length. Use that rather than carry them around by themselves. + + We also have std::span created transparantly from vectors and stuff + + poppler/CharCodeToUnicode.cc | 2 +- + poppler/TextOutputDev.cc | 4 ++-- + poppler/UTF.cc | 25 +++++++++++++------------ + poppler/UTF.h | 5 +++-- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + 6 files changed, 21 insertions(+), 19 deletions(-) + +commit 14191296ae72398638d2a4af67d783ee120a998c +Author: Albert Astals Cid +Date: Wed May 1 19:42:56 2024 +0200 + + poppler 24.05.0 + + CMakeLists.txt | 2 +- + NEWS | 28 ++++++++++++++++++++++++++++ + cpp/CMakeLists.txt | 2 +- + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 8 files changed, 35 insertions(+), 7 deletions(-) + +commit 2f11d814e54d1329985c2f60b7b6fe2d1882a9a6 +Author: Albert Astals Cid +Date: Tue Apr 23 14:15:24 2024 +0200 + + Update (C) + + poppler/Catalog.cc | 2 +- + poppler/PDFDoc.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit fe9fe140aee72a41a2add7db7f7b0dae7cb55700 +Author: Albert Astals Cid +Date: Mon Apr 22 14:22:08 2024 +0200 + + signing: Fix modifying the AcroForm dict when it's embedded in + the Catalog + + poppler/Catalog.cc | 1 + + poppler/PDFDoc.cc | 2 ++ + 2 files changed, 3 insertions(+) + +commit eac1adcbc2246fd1ad22e8336d050a71cbf03804 +Author: Albert Astals Cid +Date: Mon Apr 22 19:32:24 2024 +0200 + + Update nss to the actual release version of Ubuntu 22.04 + + CMakeLists.txt | 2 +- + cmake/modules/FindNSS3.cmake | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit ad67d43e7786eaf364c20f76c68810ad254a994d +Author: Albert Astals Cid +Date: Mon Apr 22 15:56:54 2024 +0200 + + Increase android-qt images + + Allows us increasing the min freetype + + .gitlab-ci.yml | 16 ++++++++-------- + CMakeLists.txt | 2 +- + 2 files changed, 9 insertions(+), 9 deletions(-) + +commit b81b2851feb114c2896ee0a441fe492500f0b3ca +Author: Albert Astals Cid +Date: Mon Apr 22 15:21:09 2024 +0200 + + Increase Minimum supported base to that provided by Ubuntu 22.04 + + .gitlab-ci.yml | 10 +++------- + CMakeLists.txt | 14 +++++++------- + cmake/modules/FindNSS3.cmake | 2 +- + qt5/CMakeLists.txt | 2 +- + utils/CMakeLists.txt | 7 +------ + 5 files changed, 13 insertions(+), 22 deletions(-) + +commit 06e0b89c2f64580eaac5e09a729b52668b09c106 +Author: Albert Astals Cid +Date: Mon Apr 22 10:17:03 2024 +0200 + + Fix crash in broken files + + oss-fuzz/57874 + + poppler/Object.h | 18 +++++++++++++++++- + poppler/XRef.cc | 12 +++++++++++- + poppler/XRef.h | 4 +++- + 3 files changed, 31 insertions(+), 3 deletions(-) + +commit fd98a72b6a72bb6e395ef70ae2527477496db6f5 +Author: Albert Astals Cid +Date: Mon Apr 22 10:53:15 2024 +0200 + + Fix crash in TextStringToUtf8 + + utf16ToUtf8 expects a null ended string + + poppler/UTF.cc | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +commit 103fc977d30fa6b3df8d3a018e970230e9661933 +Author: Ömer Fadıl USTA +Date: Mon Apr 22 08:56:26 2024 +0000 + + Add Missing headers to fix compile problems + + CMakeLists.txt | 2 ++ + 1 file changed, 2 insertions(+) + +commit 87fc0028b69acd8f5679a9fe213f94d081fa28e3 +Author: Albert Astals Cid +Date: Mon Apr 22 10:14:27 2024 +0200 + + Update (C) + + glib/poppler-form-field.cc | 2 +- + poppler/CryptoSignBackend.h | 2 +- + poppler/Form.cc | 2 +- + poppler/Form.h | 2 +- + poppler/GPGMECryptoSignBackend.cc | 3 ++- + poppler/GPGMECryptoSignBackend.h | 3 ++- + poppler/NSSCryptoSignBackend.cc | 2 +- + poppler/NSSCryptoSignBackend.h | 2 +- + poppler/SignatureInfo.cc | 2 +- + poppler/SignatureInfo.h | 2 +- + qt5/src/poppler-form.cc | 2 +- + qt5/src/poppler-form.h | 2 +- + qt5/tests/check_signature_basics.cpp | 2 +- + qt6/src/poppler-form.cc | 2 +- + qt6/src/poppler-form.h | 2 +- + qt6/tests/check_signature_basics.cpp | 2 +- + 16 files changed, 18 insertions(+), 16 deletions(-) + +commit d40bb7e308c9e3299e50d3e2880229cd6272587e +Author: Sune Vuorela +Date: Sun Apr 21 17:31:19 2024 +0000 + + Async api for certificate validation + + glib/poppler-form-field.cc | 7 +-- + poppler/CryptoSignBackend.h | 6 ++- + poppler/Form.cc | 51 ++++++++++++++++--- + poppler/Form.h | 23 +++++++-- + poppler/GPGMECryptoSignBackend.cc | 77 ++++++++++++++++++++++------ + poppler/GPGMECryptoSignBackend.h | 6 ++- + poppler/NSSCryptoSignBackend.cc | 63 +++++++++++++++++------ + poppler/NSSCryptoSignBackend.h | 7 ++- + poppler/SignatureInfo.cc | 10 ---- + poppler/SignatureInfo.h | 3 -- + qt5/src/poppler-form.cc | 98 + +++++++++++++++++++++++++----------- + qt5/src/poppler-form.h | 63 +++++++++++++++++++++-- + qt5/tests/check_signature_basics.cpp | 6 ++- + qt5/tests/poppler-forms.cpp | 7 ++- + qt6/src/poppler-form.cc | 98 + +++++++++++++++++++++++++----------- + qt6/src/poppler-form.h | 63 +++++++++++++++++++++-- + qt6/tests/check_signature_basics.cpp | 6 ++- + qt6/tests/poppler-forms.cpp | 2 + + utils/pdfsig.cc | 15 +++++- + 19 files changed, 477 insertions(+), 134 deletions(-) + +commit e6879d35f23e436b4fc1a55b957998a834cf1691 +Author: Albert Astals Cid +Date: Sun Apr 21 11:00:20 2024 +0200 + + Increase version + + This way people building from master can adapt to the new API already + + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 7bddce7e20ac14024ec84762668fca98a3e2c7c2 +Author: Oliver Sander +Date: Sat Apr 20 06:46:11 2024 +0200 + + Remove redundant conditional + + The condition u[i] < 0x7F was checked twice. + + utils/HtmlOutputDev.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 5aa5d29c32b404cdc4c8d1a67b3b1bc8f068976d +Author: Albert Astals Cid +Date: Sat Apr 20 16:46:55 2024 +0200 + + Update (C) + + poppler/PageLabelInfo.cc | 1 + + poppler/TextOutputDev.cc | 2 +- + poppler/UTF.h | 2 +- + 3 files changed, 3 insertions(+), 2 deletions(-) + +commit 2ce8812b658e5710ad907bb5d372c64bd61b6d57 +Author: Albert Astals Cid +Date: Wed Apr 10 01:28:46 2024 +0200 + + cpp: Fix crash extracting text and font in some files + + Issue reported and patch suggestion by Samad Koita and Aviral Agarwal + + Fixes issue #1477 + + poppler/TextOutputDev.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 941666ce0c8e20087d6c006f7bc09ecd0ec84e8f +Author: Albert Astals Cid +Date: Sat Apr 20 12:57:54 2024 +0200 + + Fix build after rebase + + poppler/PSOutputDev.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 081352cf9fa600c68ee1bebc457cee88fed6f94d +Author: Sune Vuorela +Date: Wed Apr 3 14:30:12 2024 +0200 + + pdftops: Write compliant ps header + + According to the postscript spec, only DSC Comments are allowed in the + header. + + %%Creator is the header for the software used to generate the + postscript + file, which is pdftops in this case, and not as such the generator for + the pdf file. I've chosen to, if available, keep the pdf creator as a + substring in the %%Creator field. + + Originates in + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1068307 + + poppler/PSOutputDev.cc | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +commit c5684e7697a070d7ecdf4114788ae76d57ef18b2 +Author: Stefan Brüns +Date: Tue Apr 9 21:02:39 2024 +0200 + + Add tests for fakebold text + + qt5/tests/check_actualtext.cpp | 61 + ++++++++++++++++++++++++++++++++++++++++++ + qt6/tests/check_actualtext.cpp | 61 + ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 122 insertions(+) + +commit 05121ab78f8208aa3d35cf5ccf96e84092089acc +Author: Oliver Sander +Date: Wed Apr 17 10:23:11 2024 +0200 + + Move move prependUnicodeMarker to UTF.h + + ... and rename it to prependUnicodeByteOrderMark. + + Now all unicode code has moved from GooString.h to UTF.h. + + glib/poppler-document.cc | 2 +- + goo/GooString.cc | 5 ----- + goo/GooString.h | 2 -- + poppler/Annot.cc | 12 ++++++------ + poppler/Form.cc | 6 +++--- + poppler/UTF.h | 6 ++++++ + 6 files changed, 16 insertions(+), 17 deletions(-) + +commit 1f06dca08c32ed18c3030530d98a0e30d41dd7a2 +Author: Oliver Sander +Date: Wed Apr 17 09:23:46 2024 +0200 + + Move method GooString::hasUnicodeMarkerLE to UTF.h + + ... and rename it to hasUnicodeByteOrderMarkLE. + + This allows to replace GooString by std::string in a few places. + (In a future commit) + + cpp/poppler-private.cpp | 2 +- + glib/poppler-document.cc | 2 +- + goo/GooString.h | 3 --- + poppler/UTF.cc | 2 +- + poppler/UTF.h | 11 ++++++++++- + qt5/src/poppler-private.cc | 2 +- + qt6/src/poppler-private.cc | 2 +- + 7 files changed, 15 insertions(+), 9 deletions(-) + +commit 98fabb298b0e8eaef9193bbce68c99c85473a314 +Author: Oliver Sander +Date: Mon Apr 8 07:50:40 2024 +0200 + + Move method GooString::hasUnicodeMarker to UTF.h + + ... and rename it to hasUnicodeByteOrderMark. + + This allows to replace GooString by std::string in a few places. + (In a future commit) + + cpp/poppler-private.cpp | 4 +++- + glib/poppler-document.cc | 8 ++++---- + goo/GooString.h | 2 -- + poppler/Annot.cc | 23 ++++++++++++----------- + poppler/Form.cc | 15 ++++++++------- + poppler/PDFDoc.cc | 4 ++-- + poppler/PageLabelInfo.cc | 4 ++-- + poppler/PageLabelInfo_p.h | 5 +++-- + poppler/UTF.cc | 6 +++--- + poppler/UTF.h | 6 ++++++ + qt5/src/poppler-private.cc | 5 +++-- + qt5/tests/check_pagelabelinfo.cpp | 2 +- + qt5/tests/check_strings.cpp | 4 +++- + qt6/src/poppler-private.cc | 5 +++-- + qt6/tests/check_pagelabelinfo.cpp | 2 +- + qt6/tests/check_strings.cpp | 3 ++- + utils/pdfdetach.cc | 9 +++++---- + utils/pdftohtml.cc | 5 +++-- + 18 files changed, 64 insertions(+), 48 deletions(-) + +commit c60e2ce44ae1c0af902c5139a4570d36e1b602cc +Author: Albert Astals Cid +Date: Wed Apr 10 01:07:52 2024 +0200 + + Update (C) + + poppler/Annot.cc | 2 +- + poppler/Form.cc | 2 +- + poppler/GfxFont.cc | 1 + + qt5/src/poppler-page.cc | 1 + + qt6/src/poppler-page.cc | 1 + + 5 files changed, 5 insertions(+), 2 deletions(-) + +commit a4f034830da8045981523fdad9f54b545bfd529e +Author: Nicolas Fella +Date: Mon Apr 8 16:29:23 2024 +0200 + + Fix text position in drawSignatureFieldText + + When centering vertically we calculate the y offset based on the + height of the text and the annotation + + When doing that we must ignore the border width, otherwise the text + is offset downwards + + poppler/Annot.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 750bb558f094c8f25e53f212049fa83ee8f705b9 +Author: Nicolas Fella +Date: Mon Apr 8 17:10:48 2024 +0200 + + Take border into account also for height when determining signature + font size + + The border reduces the available height, so take it into account + for the height too, not only the width + + poppler/Form.cc | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +commit 58f721067044baeee1ac56cdc679cc5e97dcbd69 +Author: Stefan Brüns +Date: Sat Mar 30 19:24:12 2024 +0100 + + Fix Qt text extraction for Landscape/Seascape pages + + TextOutputDev::getText expects rotated coordinates, e.g. the correct + bounds for an A4 Landscape page are {0, 0, 842, 595}. + + qt5/src/poppler-page.cc | 6 +++++- + qt5/tests/check_actualtext.cpp | 1 - + qt6/src/poppler-page.cc | 6 +++++- + qt6/tests/check_actualtext.cpp | 1 - + 4 files changed, 10 insertions(+), 4 deletions(-) + +commit 7ecb49e8cf696e836663afa159c4ae809b3861a7 +Author: Stefan Brüns +Date: Sat Mar 30 19:20:38 2024 +0100 + + Cover Landscape etc in Qt Page::text tests + + Currently, the "Lanscape" with default page rectangle test fails, + as the + page orientation is not taken into account. + + (Seascape is also incorrect, but as the text lies inside the unrotated + A4 cropbox rectangle (bottom left), the text is extracted.) + + qt5/tests/check_actualtext.cpp | 50 + ++++++++++++++++++++++++++++++++++++++++++ + qt6/tests/check_actualtext.cpp | 50 + ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 100 insertions(+) + +commit 0f1ad3e4c1e4baacaec1771af23e4706a2d25cff +Author: Stefan Brüns +Date: Sat Mar 30 14:21:24 2024 +0100 + + Extend unit tests for Qt Page::text functionality + + The unit tests only covered extraction from the whole page, make sure + the various cases for smaller selections are also covered. + + qt5/tests/check_actualtext.cpp | 35 ++++++++++++++++++++++++++++++----- + qt6/tests/check_actualtext.cpp | 35 ++++++++++++++++++++++++++++++----- + 2 files changed, 60 insertions(+), 10 deletions(-) + +commit a741a6ef9062ba3733f0cab7a6b1d55bec2b53df +Author: Nelson Benítez León +Date: Sat Apr 6 21:52:05 2024 +0100 + + Assume "Adobe-Identity" for character collection + + When 'CIDSystemInfo' dictionary is absent or + has invalid content, instead of aborting the font + because we cannot read the character collection, + let's assume in that case character collection + to be "Adobe-Identity". + + Fixes #1465 - Does not show text of Apple-edited PDFs + + poppler/GfxFont.cc | 22 +++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +commit bc3f42f45a15848aba686e7b493747c1a62b7c41 +Author: Albert Astals Cid +Date: Fri Apr 5 13:58:52 2024 +0200 + + Update (C) + + goo/GooString.cc | 2 +- + goo/GooString.h | 2 +- + poppler/Form.cc | 2 +- + poppler/GfxFont.cc | 2 +- + poppler/GlobalParams.cc | 2 +- + 5 files changed, 5 insertions(+), 5 deletions(-) + +commit 088d83e45c53afeb7124f741e9539a28c7240686 +Author: Oliver Sander +Date: Thu Apr 4 11:54:22 2024 +0200 + + Remove method GooString::clear + + Use std::string::clear instead. The only difference between the two + is that GooString::clear returns the empty string, whereas + std::string::clear does not. But apparently this feature of + GooString::clear was not used anywhere. + + goo/GooString.h | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +commit fbb64544e5ea25ac9b1bd25b48043d074efe9cd9 +Author: Oliver Sander +Date: Thu Apr 4 10:06:56 2024 +0200 + + Remove GooString::startsWith and GooString::endsWith + + Starting with C++20, the std::string class has methods + starts_with and ends_with, which do the same thing. + Use those instead. + + goo/GooString.cc | 10 ---------- + goo/GooString.h | 8 +++----- + poppler/Form.cc | 4 ++-- + poppler/GfxFont.cc | 2 +- + poppler/GlobalParams.cc | 6 +++--- + 5 files changed, 9 insertions(+), 21 deletions(-) + +commit 508affb41a197895ce6af32d2404b8c7b812a70f +Author: Albert Astals Cid +Date: Wed Apr 3 22:28:36 2024 +0200 + + Update (C) + + cpp/poppler-global.h | 1 + + 1 file changed, 1 insertion(+) + +commit 7f62fdb0c120f5e9d4add5b8a985e145f2b7371d +Author: Albert Astals Cid +Date: Sun Mar 31 17:52:28 2024 +0200 + + pdfdetach: find -> starts_with + + utils/pdfdetach.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit b4ac7d9af7cb5edfcfcbda035ed8b8c218ba8564 +Author: LinuxUserGD +Date: Wed Apr 3 15:21:15 2024 +0200 + + Change type to `std::basic_string` + Deprecated `char_traits` template has been removed in LLVM 19 + + cpp/poppler-global.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ff055f872aa31a548e1d73f4ea0d9394ba39d62c +Author: Stefan Brüns +Date: Mon Apr 1 16:56:26 2024 +0200 + + Regenerate pot/po files only if input has changed + + A custom target with ALL is always generated, even if the + files/outputs + specified with DPENDS are not changed. + + This can be solved by generating the POT files with a custom_command. + The target triggers evaluation of the custom_command, but the + latter will + only be run if the dependencies have changed. + + Fixes #1479 + + utils/po/CMakeLists.txt | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +commit 0aa1fe5c30a6c467c91bad8d81bd6c2f57fcb726 +Author: Albert Astals Cid +Date: Mon Apr 1 09:49:15 2024 +0200 + + poppler 24.04.0 + + CMakeLists.txt | 4 ++-- + NEWS | 16 ++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 21 insertions(+), 5 deletions(-) + +commit 199ccde44be1879bf5caa8bcbc050c1ef0327849 +Author: Nelson Benítez León +Date: Sat Mar 9 00:48:59 2024 +0000 + + reset clipping path when the state is restored + + According to the specification, see NOTE 2 in + https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf#G7.3882161 + + it appears that the clipping path should be reset + when the restore (Q) operator is encountered. + + Fixes #739 + + poppler/Gfx.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 0d5431dba10c531b6e6897e088c1c6ccec84ef55 +Author: Albert Astals Cid +Date: Sun Mar 31 17:50:50 2024 +0200 + + CI: Update the hack to change c++ standard to 23 in the clang build + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d8ae3ba659c71b4f5913ecf74d525461bb831366 +Author: Albert Astals Cid +Date: Sun Feb 4 14:06:00 2024 +0100 + + Require C++20 + + I want to use std::string::starts_with + + CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 1446d1fd2186e6573e737406c02a54c36fe1ad71 +Author: Albert Astals Cid +Date: Sun Mar 31 17:12:43 2024 +0200 + + CI: Use Fedora for clazy, it's newer and built against a newer clang + + .gitlab-ci.yml | 10 +++++++--- + poppler/FlateEncoder.cc | 2 +- + qt6/tests/check_search.cpp | 4 ++-- + 3 files changed, 10 insertions(+), 6 deletions(-) + +commit 80e0519077f74e2591d45e70801f6c3f2fe2c362 +Author: Albert Astals Cid +Date: Sun Mar 31 17:02:34 2024 +0200 + + qt: Include less files + + qt5/tests/check_actualtext.cpp | 2 +- + qt5/tests/check_annotations.cpp | 2 +- + qt5/tests/check_attachments.cpp | 2 +- + qt5/tests/check_cidfontswidthsbuilder.cpp | 2 +- + qt5/tests/check_dateConversion.cpp | 2 +- + qt5/tests/check_distinguished_name_parser.cpp | 2 +- + qt5/tests/check_fonts.cpp | 2 +- + qt5/tests/check_forms.cpp | 2 +- + qt5/tests/check_goostring.cpp | 2 +- + qt5/tests/check_internal_outline.cpp | 3 ++- + qt5/tests/check_lexer.cpp | 2 +- + qt5/tests/check_links.cpp | 2 +- + qt5/tests/check_metadata.cpp | 2 +- + qt5/tests/check_object.cpp | 2 +- + qt5/tests/check_optcontent.cpp | 2 +- + qt5/tests/check_outline.cpp | 2 +- + qt5/tests/check_overprint.cpp | 2 +- + qt5/tests/check_pagelabelinfo.cpp | 2 +- + qt5/tests/check_pagelayout.cpp | 2 +- + qt5/tests/check_pagemode.cpp | 2 +- + qt5/tests/check_password.cpp | 2 +- + qt5/tests/check_permissions.cpp | 2 +- + qt5/tests/check_search.cpp | 2 +- + qt5/tests/check_signature_basics.cpp | 2 +- + qt5/tests/check_strings.cpp | 2 +- + qt5/tests/check_stroke_opacity.cpp | 2 +- + qt5/tests/check_utf8document.cpp | 2 +- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_actualtext.cpp | 2 +- + qt6/tests/check_annotations.cpp | 2 +- + qt6/tests/check_attachments.cpp | 2 +- + qt6/tests/check_cidfontswidthsbuilder.cpp | 2 +- + qt6/tests/check_dateConversion.cpp | 2 +- + qt6/tests/check_distinguished_name_parser.cpp | 2 +- + qt6/tests/check_fonts.cpp | 2 +- + qt6/tests/check_forms.cpp | 2 +- + qt6/tests/check_goostring.cpp | 2 +- + qt6/tests/check_internal_outline.cpp | 3 ++- + qt6/tests/check_lexer.cpp | 2 +- + qt6/tests/check_links.cpp | 2 +- + qt6/tests/check_metadata.cpp | 2 +- + qt6/tests/check_object.cpp | 2 +- + qt6/tests/check_optcontent.cpp | 2 +- + qt6/tests/check_outline.cpp | 2 +- + qt6/tests/check_overprint.cpp | 2 +- + qt6/tests/check_pagelabelinfo.cpp | 2 +- + qt6/tests/check_pagelayout.cpp | 2 +- + qt6/tests/check_pagemode.cpp | 2 +- + qt6/tests/check_password.cpp | 2 +- + qt6/tests/check_permissions.cpp | 2 +- + qt6/tests/check_search.cpp | 2 +- + qt6/tests/check_signature_basics.cpp | 2 +- + qt6/tests/check_strings.cpp | 2 +- + qt6/tests/check_stroke_opacity.cpp | 2 +- + qt6/tests/check_utf8document.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + 56 files changed, 58 insertions(+), 56 deletions(-) + +commit ec2427b7cb92cda6cd7bc9b1d3117552a65d518e +Author: Albert Astals Cid +Date: Sun Mar 31 16:32:36 2024 +0200 + + CI: Use fedora 40 + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit d7e54f88c981f26b9477b2330070dccbdbbbdb55 +Author: Albert Astals Cid +Date: Sun Mar 31 11:42:11 2024 +0200 + + Update (C) + + poppler/TextOutputDev.cc | 3 ++- + poppler/TextOutputDev.h | 1 + + 2 files changed, 3 insertions(+), 1 deletion(-) + +commit f26b292412a9266aab46deb2ce1ffc4d016cc573 +Author: Stefan Brüns +Date: Thu Mar 28 02:37:09 2024 +0100 + + Reduce worst case algorithmic complexity of TextBlock::coalesce + + The old algorithm restarts the inner loop for the RHS word from the + beginning on each match, i.e. the worst case complexity approaches + O(N^3), while O(N^2) is obviously sufficient for a pairwise compare of + all words. Fortunately, O(N^2) is hardly ever happening, as the + inner N + is limited by a) the maxBaseIdx, b) removing duplicates from the set. + + For some pathological cases this changes the runtime from minutes to + seconds. + + See poppler#1173. + + poppler/TextOutputDev.cc | 109 + ++++++++++++++++++++++++++--------------------- + 1 file changed, 60 insertions(+), 49 deletions(-) + +commit 835987362d9873cf98cc3f86959910ff2107a509 +Author: Stefan Brüns +Date: Sun Mar 24 00:31:52 2024 +0100 + + Reduce TextWord space and allocation overhead + + Currently, the word characters are allocated as a struct of arrays, + e.g. text and charcode are allocated separately. + + This causes some space (6 pointers, 6 malloc chunk management + words (size_t/flags), alignment, ...) and runtime overhead (6 allocs/ + frees per word). + + Changing this to an array of struct reduces this overhead. It + also allows + to be more conservative with allocations, as resizing is less + costly, i.e. + starting with a single character allocation instead of 16. It is + also more + efficient, as most accesses affect multiple or all attributes, i.e. + values in the same or neighboring CPU cache lines. + + Using a std::vector instead of separate raw arrays also reduces code + and manual data management. + + The "charPos end index" and trailing "edge" attributes are no + longer stored as an additional entry entry in the array, but as + dedicated + data members, `charPosEnd` and `edgeEnd`. + + The memory saving is most notably for short words, but even for words + with 16 characters there are small savings, and still less allocations + (1 + 4 allocations instead of 6. Growing is fairly cheap, as the + CharInfo + struct is trivially copyable.) + + See poppler#1173. + + poppler/TextOutputDev.cc | 378 + ++++++++++++++++++++++------------------------- + poppler/TextOutputDev.h | 42 +++--- + 2 files changed, 197 insertions(+), 223 deletions(-) + +commit e803b3714a44001ac1e001d948ae505b24086b66 +Author: Albert Astals Cid +Date: Thu Feb 8 00:36:51 2024 +0100 + + Fix clang-tidy-17 "unnecessary temporary object created while calling + emplace_back" + + Says modernize-use-emplace + + No need to pass the c, we will set it later so we can just use the + default constructed CharCodeToUnicodeString + + poppler/CharCodeToUnicode.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 9ace4f33e38fe24add87dc4e7c2a43e1441f2bec +Author: Nelson Benítez León +Date: Tue Mar 12 21:37:46 2024 +0000 + + Fix text search across lines between paragraphs + + This commit fixes the "across lines" text + search feature of TextPage::findText() when + the match happens from the last line of a + paragraph to the first line of next paragraph. + + Includes tests for this bug. + + Fixes #1475 + Fixes https://gitlab.gnome.org/GNOME/evince/-/issues/2001 + + poppler/TextOutputDev.cc | 60 + +++++++++++++++++++++++++++------------------- + qt5/tests/check_search.cpp | 7 ++++++ + qt6/tests/check_search.cpp | 7 ++++++ + 3 files changed, 50 insertions(+), 24 deletions(-) + +commit 7a435135a1bfb8c3f9f5984d88bbe5dd8977335a +Author: Nelson Benítez León +Date: Sat Mar 23 12:40:07 2024 +0000 + + Fix regression on issue #157 + + Redo the fix for issue #157 which is about doing + transparent selection for glyphless documents (eg. + tesseract scanned documents) because it stopped + working after commit 29f32a47 + + poppler/TextOutputDev.cc | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +commit a10901554010bc5bbc0f24a8d14fdcdecc1b8367 +Author: Albert Astals Cid +Date: Thu Mar 28 00:05:28 2024 +0100 + + Update (C) + + qt5/src/poppler-sound.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 10377f9df110aaeb8d68df4d01f049d9cc9f9c21 +Author: Albert Astals Cid +Date: Wed Mar 27 00:43:01 2024 +0100 + + qt6: Fix crash in SoundObject::data + + qt6/src/poppler-sound.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 6a7f14bd867bc7215e1c94d27fb00c84c88a1db9 +Author: Albert Astals Cid +Date: Tue Mar 5 00:41:43 2024 +0100 + + Revert "CI: Switch debian to testing" + + This reverts commit 9c2cf5608a21b6fb9be4e0c7918d13cd2b652c23. + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 99f5416d9bfb6a34bbc5cbcb368e258b98fd3f08 +Author: Josep M. Ferrer +Date: Fri Mar 1 15:09:08 2024 +0100 + + pdfsig: Catalan translation + + utils/po/ca/CMakeLists.txt | 4 ++++ + utils/po/ca/pdfsig.po | 26 ++++++++++++++++++++++++++ + 2 files changed, 30 insertions(+) + +commit 9c2cf5608a21b6fb9be4e0c7918d13cd2b652c23 +Author: Albert Astals Cid +Date: Sun Mar 3 18:39:00 2024 +0100 + + CI: Switch debian to testing + + unstable is broken at the moment + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 481ee336d15bdf6b2b6084dddcd1617b032d2cd5 +Author: Albert Astals Cid +Date: Sun Mar 3 12:16:11 2024 +0100 + + poppler 24.03.0 + + CMakeLists.txt | 4 ++-- + NEWS | 12 ++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 17 insertions(+), 5 deletions(-) + +commit 23a3e5144e9fe5329b99b26879856e9ca3db769d +Author: Albert Astals Cid +Date: Sun Mar 3 15:36:15 2024 +0100 + + Fix srcdir=builddir build + + cmake will create folders which so we need to ignore those + + utils/po/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 840fc7637d93d40e09c7792086c96ba94811fb1f +Author: Albert Astals Cid +Date: Fri Mar 1 10:27:55 2024 +0100 + + Fix translations on non glibc platforms + + utils/CMakeLists.txt | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +commit 0ad01ca51e6b6e061ba85db5b906dfc299dba8e1 +Author: Albert Astals Cid +Date: Thu Feb 22 09:19:57 2024 +0100 + + Update (C) + + poppler/XRef.cc | 1 + + 1 file changed, 1 insertion(+) + +commit a9266633d203432234ab2dab621f61374eed2d66 +Author: Nelson Benítez León +Date: Wed Feb 21 19:56:46 2024 +0000 + + Fix regression on encrypted files being repaired + + Some encrypted files which need repairing (see + links below) failed to open due to a regression + introduced in commit b3e86dbdba where an 'if + condition' was added that's hit by encrypted files + which need repairing. + + The removal of this 'if condition' does not affect + the original buggy file that commit b3e86dbdba + targeted[1]. + + This commit also adds Qt5 and Qt6 tests for opening + an encrypted pdf file affected by this issue. + + Fixes #1447 + Fixes https://gitlab.gnome.org/GNOME/evince/-/issues/1889 + + Regression issue: + https://bugs.freedesktop.org/show_bug.cgi?id=14303 + + [1] which can be found in this duplicate: + https://bugs.freedesktop.org/show_bug.cgi?id=14399 + + poppler/XRef.cc | 2 +- + qt5/tests/check_password.cpp | 13 +++++++++++++ + qt6/tests/check_password.cpp | 10 ++++++++++ + 3 files changed, 24 insertions(+), 1 deletion(-) + +commit 87fab6144828cf52fbefb484ef7024c06ceaf6b1 +Author: Albert Astals Cid +Date: Thu Feb 22 00:23:16 2024 +0100 + + Update (C) + + poppler/Gfx.cc | 2 +- + poppler/SplashOutputDev.cc | 2 +- + poppler/Stream.cc | 2 +- + splash/Splash.cc | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +commit 3d8dac5ec9f1cdedada07c4c2fc02e43d5e14f9e +Author: Even Rouault +Date: Tue Feb 20 20:44:04 2024 +0100 + + Gfx::doImage(): avoid integer overflow if width * height > INT_MAX + + Related to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=66523 + + poppler/Gfx.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 8d66d756dad4fbde0cd1b8b0f1ce8b08caa7a19d +Author: Even Rouault +Date: Tue Feb 20 20:43:11 2024 +0100 + + SplashOutputDev::drawImageMask(): early break if getLine() fails + + Related to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=66523 + + poppler/SplashOutputDev.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 6a4d5323efd9651ab2ee5a0f921bdeb55fa61b0b +Author: Even Rouault +Date: Tue Feb 20 20:42:28 2024 +0100 + + ImageStream::getLine(): check that imgLine is not null + + Related to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=66523 + + poppler/Stream.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 30acc0bbd2c773b2709aa9138e94729439fb3025 +Author: Even Rouault +Date: Tue Feb 20 20:41:51 2024 +0100 + + Splash.cpp scaleXXXX() functions: use gmalloc[n]_checkoverflow to + avoid abort() on large memory allocations + + Related to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=66523 + + splash/Splash.cc | 151 + ++++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 132 insertions(+), 19 deletions(-) + +commit 1c7c2bae76d684bc4c8cd27f203b76009f49a848 +Author: Even Rouault +Date: Tue Feb 20 21:14:50 2024 +0100 + + Fix read-heap-buffer-overflow in Splash::blitTransparent() in + splashModeMono8 case + + Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=64471 + + ``` + $ utils/pdftoppm + clusterfuzz-testcase-minimized-gdal_fuzzer-6127122829410304 + [...] + ================================================================= + ==1758602==ERROR: AddressSanitizer: heap-buffer-overflow on + address 0x602000024cd5 at pc 0x7fd5850e977d bp 0x7ffe0e007430 sp + 0x7ffe0e007428 + READ of size 1 at 0x602000024cd5 thread T0 + #0 0x7fd5850e977c in Splash::blitTransparent(SplashBitmap*, + int, int, int, int, int, int) + /home/even/poppler/splash/Splash.cc:5778:24 + #1 0x7fd58505e19d in + SplashOutputDev::beginTransparencyGroup(GfxState*, + double const*, GfxColorSpace*, bool, bool, bool) + /home/even/poppler/poppler/SplashOutputDev.cc:3998:17 + #2 0x7fd5850451c3 in + SplashOutputDev::setSoftMaskFromImageMask(GfxState*, + Object*, Stream*, int, int, bool, bool, double*) + /home/even/poppler/poppler/SplashOutputDev.cc:2692:5 + #3 0x7fd584c3f6a7 in Gfx::doPatternImageMask(Object*, Stream*, + int, int, bool, bool) /home/even/poppler/poppler/Gfx.cc:1964:10 + #4 0x7fd584c5cc26 in Gfx::doImage(Object*, Stream*, bool) + /home/even/poppler/poppler/Gfx.cc:4304:17 + #5 0x7fd584c1827a in Gfx::opBeginImage(Object*, int) + /home/even/poppler/poppler/Gfx.cc:4900:9 + #6 0x7fd584c32abe in Gfx::execOp(Object*, Object*, int) + /home/even/poppler/poppler/Gfx.cc:811:5 + #7 0x7fd584c316ef in Gfx::go(bool) + /home/even/poppler/poppler/Gfx.cc:686:13 + #8 0x7fd584c30f76 in Gfx::display(Object*, bool) + /home/even/poppler/poppler/Gfx.cc:647:5 + #9 0x7fd58506713d in SplashOutputDev::tilingPatternFill(GfxState*, + Gfx*, Catalog*, GfxTilingPattern*, double + const*, int, int, int, int, double, double) + /home/even/poppler/poppler/SplashOutputDev.cc:4424:10 + #10 0x7fd584c3b41b in Gfx::doTilingPatternFill(GfxTilingPattern*, + bool, bool, bool) /home/even/poppler/poppler/Gfx.cc:2176:53 + #11 0x7fd584c36188 in Gfx::doPatternFill(bool) + /home/even/poppler/poppler/Gfx.cc:1895:9 + #12 0x7fd584c16d93 in Gfx::opFillStroke(Object*, int) + /home/even/poppler/poppler/Gfx.cc:1794:17 + #13 0x7fd584c32abe in Gfx::execOp(Object*, Object*, int) + /home/even/poppler/poppler/Gfx.cc:811:5 + #14 0x7fd584c316ef in Gfx::go(bool) + /home/even/poppler/poppler/Gfx.cc:686:13 + #15 0x7fd584c30f76 in Gfx::display(Object*, bool) + /home/even/poppler/poppler/Gfx.cc:647:5 + #16 0x7fd584de61b9 in Page::displaySlice(OutputDev*, double, + double, int, bool, bool, int, int, int, int, bool, bool + (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) + /home/even/poppler/poppler/Page.cc:593:14 + #17 0x7fd584dfd5fc in PDFDoc::displayPageSlice(OutputDev*, + int, double, double, int, bool, bool, bool, int, int, int, int, + bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) + /home/even/poppler/poppler/PDFDoc.cc:633:24 + #18 0x4cc9c6 in savePageSlice(PDFDoc*, SplashOutputDev*, + int, int, int, int, int, double, double, char*) + /home/even/poppler/utils/pdftoppm.cc:293:10 + #19 0x4cb932 in main /home/even/poppler/utils/pdftoppm.cc:695:9 + #20 0x7fd5841ef082 in __libc_start_main + /build/glibc-wuryBv/glibc-2.31/csu/../csu/libc-start.c:308:16 + #21 0x41d61d in _start + (/home/even/poppler/build/utils/pdftoppm+0x41d61d) + + 0x602000024cd5 is located 1 bytes to the right of 4-byte region + [0x602000024cd0,0x602000024cd4) + allocated by thread T0 here: + #0 0x495d5d in malloc + (/home/even/poppler/build/utils/pdftoppm+0x495d5d) + #1 0x7fd5849f1d54 in gmalloc(unsigned long, bool) + /home/even/poppler/goo/gmem.h:44:19 + #2 0x7fd5849f0ed0 in gmallocn(int, int, bool) + /home/even/poppler/goo/gmem.h:121:12 + #3 0x7fd584c1384d in gmallocn_checkoverflow(int, int) + /home/even/poppler/goo/gmem.h:126:12 + #4 0x7fd5850f7ec5 in SplashBitmap::SplashBitmap(int, int, int, + SplashColorMode, bool, bool, std::vector > const*) + /home/even/poppler/splash/SplashBitmap.cc:111:28 + #5 0x7fd585066631 in SplashOutputDev::tilingPatternFill(GfxState*, + Gfx*, Catalog*, GfxTilingPattern*, double + const*, int, int, int, int, double, double) + /home/even/poppler/poppler/SplashOutputDev.cc:4398:18 + #6 0x7fd584c3b41b in Gfx::doTilingPatternFill(GfxTilingPattern*, + bool, bool, bool) /home/even/poppler/poppler/Gfx.cc:2176:53 + #7 0x7fd584c36188 in Gfx::doPatternFill(bool) + /home/even/poppler/poppler/Gfx.cc:1895:9 + #8 0x7fd584c16d93 in Gfx::opFillStroke(Object*, int) + /home/even/poppler/poppler/Gfx.cc:1794:17 + #9 0x7fd584c32abe in Gfx::execOp(Object*, Object*, int) + /home/even/poppler/poppler/Gfx.cc:811:5 + #10 0x7fd584c316ef in Gfx::go(bool) + /home/even/poppler/poppler/Gfx.cc:686:13 + #11 0x7fd584c30f76 in Gfx::display(Object*, bool) + /home/even/poppler/poppler/Gfx.cc:647:5 + #12 0x7fd584de61b9 in Page::displaySlice(OutputDev*, double, + double, int, bool, bool, int, int, int, int, bool, bool + (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) + /home/even/poppler/poppler/Page.cc:593:14 + #13 0x7fd584dfd5fc in PDFDoc::displayPageSlice(OutputDev*, + int, double, double, int, bool, bool, bool, int, int, int, int, + bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) + /home/even/poppler/poppler/PDFDoc.cc:633:24 + #14 0x4cc9c6 in savePageSlice(PDFDoc*, SplashOutputDev*, + int, int, int, int, int, double, double, char*) + /home/even/poppler/utils/pdftoppm.cc:293:10 + #15 0x4cb932 in main /home/even/poppler/utils/pdftoppm.cc:695:9 + #16 0x7fd5841ef082 in __libc_start_main + /build/glibc-wuryBv/glibc-2.31/csu/../csu/libc-start.c:308:16 + + SUMMARY: AddressSanitizer: heap-buffer-overflow + /home/even/poppler/splash/Splash.cc:5778:24 in + Splash::blitTransparent(SplashBitmap*, int, int, int, int, int, int) + ``` + + splash/Splash.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit a938d58fb5b7ab43447f972ce238618fe2534ccd +Author: Albert Astals Cid +Date: Sun Feb 18 19:25:43 2024 +0100 + + Update (C) + + poppler/Annot.cc | 2 +- + poppler/Annot.h | 1 + + poppler/DateInfo.cc | 1 + + poppler/DateInfo.h | 1 + + poppler/Form.cc | 2 +- + utils/pdfsig.cc | 2 +- + 6 files changed, 6 insertions(+), 3 deletions(-) + +commit a7f1652ebdb78a4757565a91b62294ad7333ec75 +Author: Erich E. Hoover +Date: Thu Feb 8 11:07:24 2024 -0700 + + pdfsig: Sign form signature fields with an appearance + + utils/CMakeLists.txt | 7 +++++++ + utils/pdfsig.1 | 3 +++ + utils/pdfsig.cc | 33 ++++++++++++++++++++++++++++++++- + utils/po/CMakeLists.txt | 26 ++++++++++++++++++++++++++ + utils/po/pdfsig.pot | 26 ++++++++++++++++++++++++++ + 5 files changed, 94 insertions(+), 1 deletion(-) + +commit 53b98e8d3c7783b4340e6006e7641fa6b0ffe3ec +Author: Erich E. Hoover +Date: Mon Aug 15 10:04:17 2022 -0600 + + DateInfo: Add timeToStringWithFormat for using a custom format string + + poppler/DateInfo.cc | 33 +++++++++++++++++++++++++-------- + poppler/DateInfo.h | 6 ++++++ + 2 files changed, 31 insertions(+), 8 deletions(-) + +commit 7b189dc13c1e8de7df23326ce5b2ff93b76dc712 +Author: Erich E. Hoover +Date: Wed Oct 26 11:33:48 2022 -0600 + + Form: Allow signDocumentWithAppearance to autocalculate the font size + + poppler/Form.cc | 34 +++++++++++++++++++++++++++++++++- + 1 file changed, 33 insertions(+), 1 deletion(-) + +commit 4a278246628850d5b7dc0ee3f6cbc9d38999e85a +Author: Erich E. Hoover +Date: Wed Oct 26 11:34:21 2022 -0600 + + Annot: Pull out the font size calculator into a separate routine + + poppler/Annot.cc | 54 + +++++++++++++++++++++++++++++++----------------------- + poppler/Annot.h | 2 ++ + 2 files changed, 33 insertions(+), 23 deletions(-) + +commit 4b109c2fdcd1d170d329178dd2f33ae55cc3fef0 +Author: Albert Astals Cid +Date: Thu Feb 15 23:28:06 2024 +0100 + + Update (C) + + utils/ImageOutputDev.cc | 1 + + utils/ImageOutputDev.h | 1 + + utils/pdfimages.cc | 1 + + 3 files changed, 3 insertions(+) + +commit 90726f1e54c3be924f8a0419fc6fca4657127d89 +Author: Sebastian J. Bronner +Date: Thu Feb 15 22:27:08 2024 +0000 + + Enable pdfimages to print filenames to stdout. + + Add a command line option -print-filenames so that after writing each + imaage to file, pdfimages will print the filename to stdout. This + is in + line with the --batch-print option of scanimage and the + stream_filelist + parameter of tesseract. It allows easy parallelization of different + stages of an image processing pipeline. + + utils/ImageOutputDev.cc | 5 +++++ + utils/ImageOutputDev.h | 4 ++++ + utils/pdfimages.1 | 3 +++ + utils/pdfimages.cc | 3 +++ + 4 files changed, 15 insertions(+) + +commit 2934e3127adaff049224c741ddaeef8de832c6ab +Author: Albert Astals Cid +Date: Tue Feb 13 22:59:19 2024 +0100 + + Update (C) + + poppler/Gfx.cc | 1 + + 1 file changed, 1 insertion(+) + +commit a4da948c19990e299dd336a30d32760ce90b0136 +Author: Nelson Benítez León +Date: Tue Feb 13 10:28:22 2024 +0000 + + Gfx::doImage: skip drawing image when it has singular matrix + + otherwise it will result in broken output in Cairo backend. + + Splash backend already works fine for this case because + it checks for singular matrix in Splash::drawImage(). + + This commit adds that check early in Gfx::doImage() + which fixes the Cairo backend and for Splash backend + means a perf improvement by avoiding lot of color + computation and image preparation done in + SplashOutputDev::draw{Image,ImageMask,MaskedImage,softMaskedImage} + prior to calling Splash::drawImage which is the one + that checks singular matrix and skips. + + Note: singular matrix case is not mentioned in PDF spec + but Xpdf and other pdf readers de-facto do as in here + i.e. skip drawing an image when it has a singular (non + invertible) matrix. + + Fixes issue #1114 + + poppler/Gfx.cc | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +commit 0672a083a5f8e0c5ca53f7cf0dd967ce78b26fd1 +Author: Albert Astals Cid +Date: Thu Feb 8 19:00:01 2024 +0100 + + Update (C) + + poppler/FileSpec.cc | 1 + + poppler/FileSpec.h | 1 + + utils/pdfdetach.cc | 3 ++- + 3 files changed, 4 insertions(+), 1 deletion(-) + +commit d169ac132d36ba1459000a27cada084e1633859a +Author: Albert Astals Cid +Date: Sun Feb 4 10:31:43 2024 +0100 + + Use std::filesystem + + utils/pdfdetach.cc | 40 ++++++++++++++++++++++++---------------- + 1 file changed, 24 insertions(+), 16 deletions(-) + +commit d8e7a8a9dd4ddd642c35f5fd1538396be2a68aa9 +Author: Sune Vuorela +Date: Fri Jun 30 15:25:14 2023 +0200 + + Fix directory traversal in pdfdetach + + A carefully crafted pdf file could lead to writing files in wrong + places + of the file system by using pdfdetach. + + Thanks to jwilk for spotting the issue. + + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1026908 + + poppler/FileSpec.cc | 4 ++-- + poppler/FileSpec.h | 2 +- + utils/pdfdetach.cc | 66 + ++++++++++++++++++++++++++--------------------------- + 3 files changed, 35 insertions(+), 37 deletions(-) + +commit 6e3824d45d42cb806a28a2df84e4ab6bb3587083 +Author: Albert Astals Cid +Date: Thu Feb 8 00:57:02 2024 +0100 + + Use an enum for Function getType + + poppler/Function.cc | 4 ++-- + poppler/Function.h | 29 ++++++++++++++++------------- + poppler/PSOutputDev.cc | 12 ++++++------ + 3 files changed, 24 insertions(+), 21 deletions(-) + +commit 925b104c33f5a1a546c72794d30a6deb918998f7 +Author: Sune Vuorela +Date: Fri Feb 2 11:18:42 2024 +0100 + + Use more vectors and less owning pointers + length + + Also remove a couple of unreferenced functions. + + poppler/CharCodeToUnicode.cc | 175 + ++++++++----------------------------------- + poppler/CharCodeToUnicode.h | 16 +--- + 2 files changed, 35 insertions(+), 156 deletions(-) + +commit d44142d3bb95997af339ebe7d30d2180fe74d6f4 +Author: Sune Vuorela +Date: Mon Feb 5 10:43:01 2024 +0100 + + Microoptimization in parsehex + + parsehex is quite a hot codepath when loading documents. + + Various experiments shown that this let the compiler generate slightly + faster code. + + poppler/CharCodeToUnicode.cc | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +commit 7e66b9afe16e7eb559655d7c400cf4c1e6e04d26 +Author: Sune Vuorela +Date: Fri Feb 2 10:36:44 2024 +0100 + + inline UnicodeIsValid + + While profiling document loading, a lot was hitting this function; try + let the compiler be smarter. + + poppler/UTF.cc | 5 ----- + poppler/UTF.h | 5 ++++- + 2 files changed, 4 insertions(+), 6 deletions(-) + +commit 5ad744922aeed83ecf2bbdb83c783bbfc23b4192 +Author: Albert Astals Cid +Date: Thu Feb 1 23:44:27 2024 +0100 + + poppler 24.02.0 + + CMakeLists.txt | 4 ++-- + NEWS | 11 +++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 16 insertions(+), 5 deletions(-) + +commit c80ae496d04ebcc94d480e5ba9a1d35a32f03719 +Author: Albert Astals Cid +Date: Thu Feb 1 20:20:52 2024 +0100 + + Update (C) + + cpp/poppler-toc.cpp | 1 + + glib/poppler-document.cc | 1 + + poppler/CharCodeToUnicode.cc | 1 + + poppler/CharCodeToUnicode.h | 1 + + poppler/DateInfo.cc | 1 + + poppler/JSInfo.cc | 1 + + poppler/Outline.cc | 1 + + poppler/Outline.h | 1 + + poppler/TextOutputDev.cc | 1 + + poppler/UTF.cc | 2 +- + poppler/UTF.h | 2 +- + qt5/src/poppler-outline.cc | 1 + + qt5/src/poppler-private.cc | 1 + + qt5/src/poppler-private.h | 1 + + qt6/src/poppler-outline.cc | 1 + + qt6/src/poppler-private.cc | 1 + + qt6/src/poppler-private.h | 1 + + utils/HtmlFonts.cc | 1 + + utils/HtmlFonts.h | 1 + + utils/HtmlOutputDev.cc | 1 + + utils/HtmlOutputDev.h | 1 + + utils/pdfinfo.cc | 1 + + utils/pdfsig.cc | 2 +- + utils/pdftohtml.cc | 1 + + 24 files changed, 24 insertions(+), 3 deletions(-) + +commit fc1c711cb5f769546c6b31cc688bf0ee7f0c1dbc +Author: Sune Vuorela +Date: Thu Feb 1 19:11:03 2024 +0000 + + More unicode vectors; fewer raw pointers + + cpp/poppler-toc.cpp | 5 +- + glib/poppler-document.cc | 3 +- + poppler/CharCodeToUnicode.cc | 113 + ++++++++++++----------------------- + poppler/CharCodeToUnicode.h | 12 ++-- + poppler/DateInfo.cc | 10 ++-- + poppler/JSInfo.cc | 9 +-- + poppler/Outline.cc | 12 +--- + poppler/Outline.h | 7 +-- + poppler/TextOutputDev.cc | 8 +-- + poppler/UTF.cc | 78 +++++++++++------------- + poppler/UTF.h | 11 ++-- + qt5/src/poppler-outline.cc | 2 +- + qt5/src/poppler-private.cc | 13 ++-- + qt5/src/poppler-private.h | 1 + + qt5/tests/check_internal_outline.cpp | 6 +- + qt5/tests/check_utf8document.cpp | 3 +- + qt5/tests/check_utf_conversion.cpp | 24 ++++---- + qt6/src/poppler-outline.cc | 2 +- + qt6/src/poppler-private.cc | 5 ++ + qt6/src/poppler-private.h | 1 + + qt6/tests/check_internal_outline.cpp | 6 +- + qt6/tests/check_utf8document.cpp | 3 +- + qt6/tests/check_utf_conversion.cpp | 24 ++++---- + utils/HtmlFonts.cc | 4 +- + utils/HtmlFonts.h | 2 +- + utils/HtmlOutputDev.cc | 38 ++++++------ + utils/HtmlOutputDev.h | 2 +- + utils/pdfinfo.cc | 8 +-- + utils/pdfsig.cc | 8 +-- + utils/pdftohtml.cc | 25 +++----- + 30 files changed, 187 insertions(+), 258 deletions(-) + +commit e56d7d7d325928e4db43043eec9c290ef0e690e3 +Author: Albert Astals Cid +Date: Wed Jan 31 23:46:47 2024 +0100 + + Update (C) + + poppler/GlobalParams.cc | 1 + + 1 file changed, 1 insertion(+) + +commit c1443b5fdb62c5d02e448933aaeac7a080831cde +Author: Keyu Tao +Date: Wed Jan 3 12:47:17 2024 +0800 + + Use FcPatternGetCharSet to fast skip fonts in + findSystemFontFileForUChar + + poppler/GlobalParams.cc | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +commit af05d3064f459550a361b3f30e3efb0598546948 +Author: Albert Astals Cid +Date: Thu Jan 25 23:51:05 2024 +0100 + + Fix setting annotpolygon interior color to empty + + poppler/Annot.cc | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +commit b0d437040cbfa65604d98a30bb2158edf2a2c053 +Author: Albert Astals Cid +Date: Thu Jan 25 23:49:53 2024 +0100 + + Fix saving annotgeometry interior color when it's empty + + KDE bug #479732 + + poppler/Annot.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 362e45c7865835a7b249dee4e63b17110f1fb6bb +Author: Nelson Benítez León +Date: Thu Jan 25 16:58:17 2024 +0000 + + Remove unneeded comment + + poppler/Stream.h | 1 - + 1 file changed, 1 deletion(-) + +commit 2b2d5bc0a7351895c662958105dac11adfecf794 +Author: Albert Astals Cid +Date: Thu Jan 25 23:49:11 2024 +0100 + + Update (C) + + poppler/JBIG2Stream.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 42004cb3278f75403c12bc38bae147937c16ea90 +Author: Nelson Benítez León +Date: Thu Jan 25 16:23:23 2024 +0000 + + Fix small oversight from b8de1a191 + + There was one 'bytecounter increase' case + that was not imported from the Xpdf code. + + That caused some JPEG streams fail to + render when hitting that codepath, like + the file 'p1.blank_with_poppler.pdf' + posted on issue #1319 + + Fixes #1319 + + poppler/JBIG2Stream.cc | 1 + + 1 file changed, 1 insertion(+) + +commit a93aa6acf6efe14a5f0494552b0e56db5085df35 +Author: Albert Astals Cid +Date: Thu Jan 25 00:12:23 2024 +0100 + + Add a few const and reduce variable scopes + + utils/ImageOutputDev.h | 4 ++-- + utils/pdfimages.cc | 14 +++++--------- + 2 files changed, 7 insertions(+), 11 deletions(-) + +commit 7c9dfc10fd7b3ee3bdd1a71ab932ffd93dfebeba +Author: Albert Astals Cid +Date: Thu Jan 25 00:13:22 2024 +0100 + + Update (C) + + poppler/XRef.h | 2 +- + utils/ImageOutputDev.cc | 1 + + utils/ImageOutputDev.h | 1 + + utils/pdfimages.cc | 1 + + 4 files changed, 4 insertions(+), 1 deletion(-) + +commit b20632277c6352306651f5625ec43172c470a55c +Author: Fernando Herrera +Date: Wed Jan 24 22:33:02 2024 +0000 + + pdfimages: return exit code 2 when error opening output files + + Issue #1460 + + utils/ImageOutputDev.cc | 8 +++++++- + utils/ImageOutputDev.h | 8 ++++++-- + utils/pdfimages.cc | 5 +++-- + 3 files changed, 16 insertions(+), 5 deletions(-) + +commit 25ffb7b7ac82d8d2b394514149014142a5160096 +Author: Sune Vuorela +Date: Wed Jan 24 10:58:27 2024 +0100 + + Make stream compression non-optional + + Requires the users to think if their stream is compressed or not + and if it is a good idea to compress or not, rather than default to + 'not + compressed' + + poppler/XRef.h | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit c7ad20c2eac29d01bdeff12ac0c936ed882ed826 +Author: Albert Astals Cid +Date: Tue Jan 23 23:47:56 2024 +0100 + + Update (C) + + poppler/ImageEmbeddingUtils.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 90485ab0a42cb2ec9adcea1308ca11dc2e09572f +Author: Sune Vuorela +Date: Tue Jan 23 22:44:06 2024 +0000 + + Compress images when added + + poppler/ImageEmbeddingUtils.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 6a4c667ee4096ba06e46d86bfc2b18a67e346303 +Author: Albert Astals Cid +Date: Tue Jan 23 00:02:05 2024 +0100 + + Update (C) + + poppler/TextOutputDev.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 2158361781699bf6dad451ac1a929f8f328c7413 +Author: Adam Sampson +Date: Thu Jan 18 00:01:15 2024 +0000 + + TextPage::takeText: reset actualText for the new page + + actualText has an internal pointer to the TextPage it's writing to, so + if you called takeText and then continued to output more pages to the + TextOutputDev, their text would be written to the page you'd taken + rather than the new one. + + poppler/TextOutputDev.cc | 2 ++ + 1 file changed, 2 insertions(+) + +commit 87963f675f3532073c1d71c7ba298289485bc7f7 +Author: Albert Astals Cid +Date: Tue Jan 2 22:40:38 2024 +0100 + + poppler 24.01.0 + + CMakeLists.txt | 4 ++-- + NEWS | 7 +++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 12 insertions(+), 5 deletions(-) + +commit 38b471d3796efd4a7a01c5a31c1e40606f355213 +Author: Albert Astals Cid +Date: Tue Jan 2 22:36:39 2024 +0100 + + Welcome 2024 + + poppler/poppler-config.h.cmake | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit f1c69969e031687e6d991677dda021a2a4db48e3 +Author: Albert Astals Cid +Date: Fri Dec 29 17:29:56 2023 +0100 + + Fix infinite loop in HorizontalTextLayouter if there's not enough + space to layout text #2 + + Happens only if the first chracter we're asking to draw can't be drawn + with the given font and we need to find a new one and the given + available space is negative (as said this function must always + layout at + least one character) + + poppler/Annot.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 8a9f1d3a84a9f3d66cd353b7fe1aef0b65a37c08 +Author: Albert Astals Cid +Date: Thu Dec 28 18:35:21 2023 +0100 + + Fix infinite loop in HorizontalTextLayouter if there's not enough + space to layout text + + Happens only if the first chracter we're asking to draw can't be drawn + with the given font and we need to find a new one + + poppler/Annot.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 4be9ecdadcacf52dc0e413c30d09a61384a36166 +Author: Albert Astals Cid +Date: Sat Dec 23 01:00:02 2023 +0100 + + Update (C) + + poppler/FontInfo.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 4fb6a9bc495fa7059e7eed8adb5d46bed44809be +Author: suzuki toshiya +Date: Fri Dec 8 17:16:38 2023 +0900 + + Initialize FontInfo::embRef by Ref::INVALID() for Type3 font. + + poppler/FontInfo.cc | 1 + + 1 file changed, 1 insertion(+) + +commit afaddf1be66aed2931a146bb6555225c82cceacd +Author: Albert Astals Cid +Date: Thu Dec 14 00:48:45 2023 +0100 + + NSSCryptoSignBackend: Don't crash on certain documents + + poppler/NSSCryptoSignBackend.cc | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +commit 37023cdfc15600cd58303147dc99254c119c51c4 +Author: Albert Astals Cid +Date: Wed Dec 13 23:08:44 2023 +0100 + + Update (C) + + poppler/JPEG2000Stream.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 6fa6d41d046e440165e899a84a38da2c1ec146ed +Author: Albert Astals Cid +Date: Sun Dec 10 23:44:42 2023 +0100 + + CI: Add android generic build + + .gitlab-ci.yml | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +commit cf9210568ebd45de13e2f225090df1c9bdfa8248 +Author: Albert Astals Cid +Date: Sun Dec 10 23:49:15 2023 +0100 + + Fix build on Android with generic font configuration + + poppler/GlobalParams.cc | 2 ++ + 1 file changed, 2 insertions(+) + +commit 3d870122d70c3b3327d93eb761eb14003cc745a7 +Author: Albert Astals Cid +Date: Sun Dec 10 22:40:58 2023 +0100 + + We require an openjpeg >= 2.1 + + poppler/JPEG2000Stream.cc | 15 --------------- + 1 file changed, 15 deletions(-) + +commit 4634bfdaeb46bb981413f247b1cfe82674a5f113 +Author: Albert Astals Cid +Date: Sat Dec 9 01:17:21 2023 +0100 + + CI: Fix build_clang16_libcpp + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 6748cfc84ffef2bfa2328f1da7c70a99d93e0807 +Author: Albert Astals Cid +Date: Fri Dec 1 10:31:59 2023 +0100 + + poppler 23.12.0 + + CMakeLists.txt | 2 +- + NEWS | 5 +++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 9 insertions(+), 4 deletions(-) + +commit 84b359b82ee5b10ae714a0fbf3fda53f73e88722 +Author: Albert Astals Cid +Date: Thu Sep 28 00:27:54 2023 +0200 + + Rewrite FoFiType1::parse to be more flexible + + Previous code needed Encoding to be at the start of it's own line + + The file at issue #1422 doesn't have that and most of the other + renderers show it correctly so we should do the same. + + Also the code should be easier to understand now, and probably + faster since we're not copying string around like before + + fofi/FoFiType1.cc | 295 + ++++++++++++++++++++--------------------------------- + fofi/FoFiType1.h | 12 +-- + poppler/GfxFont.cc | 9 +- + 3 files changed, 120 insertions(+), 196 deletions(-) + +commit 0c53de52deb1328d4d45ab3dfc742acb6386a369 +Author: Albert Astals Cid +Date: Thu Nov 9 23:56:12 2023 +0100 + + Update (C) + + poppler/Object.h | 1 + + utils/pdfunite.cc | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 73c7458073429371ef3a9ed4f7edd6060d1f822d +Author: Oliver Sander +Date: Fri Nov 3 09:04:58 2023 +0100 + + Lexer: Allocate strings on the stack, not on the heap + + Makes the code easier to read, and possibly even a bit faster, too. + + poppler/Lexer.cc | 60 + +++++++++++++++++++++----------------------------------- + 1 file changed, 22 insertions(+), 38 deletions(-) + +commit 6d37f787abc05a658d342d42995c5df167c5e5b9 +Author: Oliver Sander +Date: Fri Nov 3 15:30:15 2023 +0100 + + Object: Add constructor from r-value std::string + + This can simplify the calling code when the string to use + is stack-allocated. + + Unfortunately, the new constructor has to do a heap allocation + internally, because Object stores a pointer to a string, + not a string value. + + With the new constructor, construction of Object objects from + std::initializer_list is now ambiguous. This leads to compiler + errors in a few places where {num, gen} is used to initialize + an Object with a Ref object. The patch replaces this construction + by Ref{num, gen}, which fixes the problem. + + poppler/Object.h | 5 +++++ + qt5/tests/check_optcontent.cpp | 4 ++-- + qt6/tests/check_optcontent.cpp | 4 ++-- + utils/pdfunite.cc | 8 ++++---- + 4 files changed, 13 insertions(+), 8 deletions(-) + +commit 975d6998b0ad979e81ea133be493bbf7be113fc7 +Author: Albert Astals Cid +Date: Wed Nov 1 22:47:06 2023 +0100 + + poppler 23.11.0 + + CMakeLists.txt | 4 ++-- + NEWS | 10 ++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 15 insertions(+), 5 deletions(-) + +commit 3caa03d36f78ddeac1a8571a2e789afdfb5f154d +Author: Albert Astals Cid +Date: Sun Oct 22 17:13:38 2023 +0200 + + Update (C) + + poppler/CairoOutputDev.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 4e86f71b98c45bae7b2304eca630cf2ae24c216d +Author: Albert Astals Cid +Date: Sun Oct 22 17:11:01 2023 +0200 + + Simple GooString -> std::string change + + poppler/Annot.cc | 2 +- + poppler/Annot.h | 4 ++-- + poppler/Form.cc | 2 +- + poppler/Form.h | 2 +- + poppler/Link.cc | 6 +++--- + poppler/Link.h | 4 ++-- + 6 files changed, 10 insertions(+), 10 deletions(-) + +commit 9e174595d0e891188275420cf045ddf423a1ecbc +Author: Albert Astals Cid +Date: Wed Oct 11 18:23:07 2023 +0200 + + Fix crash on broken files + + Bug #1441 + + poppler/CairoOutputDev.cc | 28 +++++++++++++++------------- + 1 file changed, 15 insertions(+), 13 deletions(-) + +commit f0adfa56e54480f6e41baa89eb6e9b931d6a8359 +Author: Anton Thomasson +Date: Sun Sep 24 21:47:16 2023 +0200 + + Use internal downscaling algorithm if image exceeds Cairo's maximum + dimensions. + + poppler/CairoOutputDev.cc | 19 ++++++++----------- + 1 file changed, 8 insertions(+), 11 deletions(-) + +commit 4d45d3304a94bc70d5f8a74dcc30b24dc7e54a45 +Author: Albert Astals Cid +Date: Sun Oct 22 16:47:57 2023 +0200 + + Use RefRecursionChecker instead std::set + + Much simpler code + + poppler/Catalog.cc | 26 +++++++++----------------- + poppler/Catalog.h | 4 ++-- + 2 files changed, 11 insertions(+), 19 deletions(-) + +commit c45800a26a1c7c511cb088b2078c7beff3701f63 +Author: Albert Astals Cid +Date: Wed Oct 18 01:17:33 2023 +0200 + + Update (C) + + goo/GooString.cc | 2 +- + poppler/PDFDoc.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 611a0cc0f0a4a866a84dcbd4870d30a9b2566365 +Author: Oliver Sander +Date: Sun Oct 15 18:22:12 2023 +0200 + + Remove method GooString::hasJustUnicodeMarker + + Because it is used only once, and writing "== unicodeByteOrderMark" + instead is just as short and readable. + + goo/GooString.h | 3 +-- + poppler/PDFDoc.cc | 4 ++-- + 2 files changed, 3 insertions(+), 4 deletions(-) + +commit 28e70bc0fafe1499b107d5965f1e8c7a59b4c4a5 +Author: Oliver Sander +Date: Sun Oct 15 20:54:16 2023 +0200 + + Move the method sanitizedName from GooString to PDFDoc + + Because it is only used by the PDFDoc class, and it does not seem + to be generic enough for a GooString method. + + Also, this patch modifies the method to return the string as a + std::string object by value, rather than as a pointer to a + heap-allocated GooString object. This saves one heap allocation. + + goo/GooString.cc | 17 ----------------- + goo/GooString.h | 5 ----- + poppler/PDFDoc.cc | 25 +++++++++++++++++++------ + poppler/PDFDoc.h | 5 +++++ + 4 files changed, 24 insertions(+), 28 deletions(-) + +commit c89b933f3bd235c6f95b420fdcfe5ca7df900c09 +Author: Albert Astals Cid +Date: Sat Oct 14 00:27:15 2023 +0200 + + Update (C) + + poppler/Annot.h | 2 +- + poppler/Gfx.cc | 2 +- + poppler/MarkedContentOutputDev.cc | 2 +- + poppler/MarkedContentOutputDev.h | 2 +- + poppler/OutputDev.h | 2 +- + poppler/Page.cc | 2 +- + poppler/Page.h | 2 +- + poppler/StructElement.cc | 2 +- + poppler/StructElement.h | 2 +- + poppler/UTF.cc | 2 +- + poppler/UTF.h | 2 +- + utils/pdftocairo.cc | 2 +- + 12 files changed, 12 insertions(+), 12 deletions(-) + +commit 760242166a8dacff674b2039ba27d823f0c27bac +Author: Adrian Johnson +Date: Thu Oct 12 22:21:33 2023 +0000 + + pdftocairo: EPS output should not contain %%PageOrientation + + utils/pdftocairo.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit b4052d02b44e8e412316a8dca1a99f9714e5aa8e +Author: Adrian Johnson +Date: Mon Oct 2 12:25:22 2023 +1030 + + cairo: write document logical structure if output is pdf + + Cairo 1.18 can create a tagged pdf. Add support to CairoOutputDev to + copy the logical structure from the input pdf if available. + + Added setLogicalStructure() to enable. + + Added -struct option to pdftocairo to enable. + + poppler/Annot.h | 2 +- + poppler/CairoOutputDev.cc | 472 + +++++++++++++++++++++++++++++++++++++- + poppler/CairoOutputDev.h | 50 ++++ + poppler/Gfx.cc | 4 +- + poppler/MarkedContentOutputDev.cc | 4 +- + poppler/MarkedContentOutputDev.h | 4 +- + poppler/OutputDev.h | 4 +- + poppler/Page.cc | 9 + + poppler/Page.h | 5 + + poppler/StructElement.cc | 9 + + poppler/StructElement.h | 5 +- + poppler/UTF.cc | 32 +++ + poppler/UTF.h | 5 + + utils/pdftocairo.1 | 4 + + utils/pdftocairo.cc | 46 +++- + 15 files changed, 635 insertions(+), 20 deletions(-) + +commit 48914b5d5fc12ae96d4f3ac3fc9c6fd08a1d6496 +Author: Albert Astals Cid +Date: Mon Oct 2 21:39:58 2023 +0200 + + poppler 23.10.0 + + CMakeLists.txt | 4 ++-- + NEWS | 10 ++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 15 insertions(+), 5 deletions(-) + +commit a678acd1224e40140980ec05b117e3fcded36246 +Author: Sune Vuorela +Date: Fri Sep 29 13:49:03 2023 +0200 + + Check if linker supports version scripts + + This is at least not fully supported on solaris. + + CMakeLists.txt | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +commit 6789f6fb2774940c2b60d71850826f0dad988167 +Author: Albert Astals Cid +Date: Thu Sep 28 00:57:00 2023 +0200 + + CI: Use fedora 39 + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 0832cb20ebb224aed388ab3d6ff90e5e2cbe394e +Author: Albert Astals Cid +Date: Thu Sep 28 00:42:05 2023 +0200 + + CI: Use clang 16 + + .gitlab-ci.yml | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +commit c673208da4144429f2e43b4ad47c349ebda38691 +Author: Albert Astals Cid +Date: Wed Sep 27 19:09:02 2023 +0200 + + FoFiType1::parse: Also keep parsing if !gotMatrix + + We probably forgot to add it in + 876021b1aa16ad38767a91e1be31c392f368fde2 + + fofi/FoFiType1.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit bbf225296bcb698713fda0d144e9d86ea2e28c55 +Author: Albert Astals Cid +Date: Tue Sep 26 22:55:30 2023 +0200 + + Use RefRecursionChecker in StructElement + + poppler/StructElement.cc | 11 +++++------ + poppler/StructElement.h | 8 ++++---- + poppler/StructTreeRoot.cc | 6 +++--- + 3 files changed, 12 insertions(+), 13 deletions(-) + +commit 1f8500587a5970879ddc282d0ae041991db6e6a6 +Author: Albert Astals Cid +Date: Mon Sep 25 17:34:12 2023 +0200 + + CI: Add android Qt6 CI + + .gitlab-ci.yml | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +commit a28fdcb8c7247d3b84363e0f5de82e493a50b185 +Author: Albert Astals Cid +Date: Mon Sep 25 18:37:13 2023 +0200 + + Update (C) + + splash/Splash.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit d0440bf2b525dcb3372802a5b5b621a3e2d5be6e +Author: Albert Astals Cid +Date: Mon Sep 25 18:07:34 2023 +0200 + + Use zu for printf + size_t + + splash/Splash.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 991470945f8f09b4a675e45df1223110eac1a9d8 +Author: Adrian Johnson +Date: Mon Sep 25 10:27:10 2023 +0000 + + cairo: update type 3 fonts for cairo 1.18 api + + poppler/CairoFontEngine.cc | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +commit 60dc1dd072565f0999519ead607f2b4e93feb8b5 +Author: Albert Astals Cid +Date: Mon Sep 25 11:38:16 2023 +0200 + + CI: -DENABLE_GPGME=OFF for glib_docs + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 8646a6aa2cb60644b56dc6e6e3b3af30ba920245 +Author: Albert Astals Cid +Date: Tue Aug 15 23:44:59 2023 +0200 + + Make a few more dependencies soft-mandatory + + they can be disabled via cmake option + + .gitlab-ci.yml | 16 +++++++---- + CMakeLists.txt | 86 + ++++++++++++++++++++++++++-------------------------------- + 2 files changed, 49 insertions(+), 53 deletions(-) + +commit 49cc0c4ccbc7776454cd5c7dd4136f52e3d545c8 +Author: Albert Astals Cid +Date: Wed Sep 20 22:25:35 2023 +0200 + + Update (C) + + poppler/Annot.cc | 2 +- + poppler/FontInfo.cc | 2 +- + poppler/FontInfo.h | 2 +- + poppler/Object.h | 2 +- + poppler/PageLabelInfo.cc | 2 +- + poppler/PageLabelInfo.h | 2 +- + 6 files changed, 6 insertions(+), 6 deletions(-) + +commit e899dc75eb692f7496b9b4d683916c3626eadc02 +Author: Albert Astals Cid +Date: Wed Sep 20 22:20:55 2023 +0200 + + FontInfoScanner::scanFonts: Fix infinite recursion on broken files + + poppler/FontInfo.cc | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +commit 8ba682b1a5d53efe975849a260e78715da74263c +Author: Albert Astals Cid +Date: Wed Sep 20 22:20:13 2023 +0200 + + Introduce RefRecursionChecker + + poppler/Annot.cc | 17 +++-------------- + poppler/FontInfo.cc | 15 +++++---------- + poppler/FontInfo.h | 2 +- + poppler/Object.h | 22 ++++++++++++++++++++++ + poppler/PageLabelInfo.cc | 14 +++++--------- + poppler/PageLabelInfo.h | 2 +- + 6 files changed, 37 insertions(+), 35 deletions(-) + +commit 77573e1a706f861ca4677ea36dbdcc0a9fb50a99 +Author: Albert Astals Cid +Date: Wed Sep 20 00:32:15 2023 +0200 + + Fix DCTStream getting stuck in some broken files + + Fixes #1430 + + poppler/DCTStream.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit e6be3e11ada80291d5f9aa74dbe1604ff20c7cd1 +Author: Albert Astals Cid +Date: Mon Sep 18 23:41:53 2023 +0200 + + Update (C) + + poppler/XRef.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 6591a88c05285a7678dd8e7299d064f052096665 +Author: Albert Astals Cid +Date: Tue Sep 12 23:08:51 2023 +0200 + + PDFDoc::savePageAs: Use more unique_ptr + + poppler/PDFDoc.cc | 65 + ++++++++++++++++++++++--------------------------------- + 1 file changed, 26 insertions(+), 39 deletions(-) + +commit d37735a209da2b8b62e18afc7ab0f7db8a28f965 +Author: Even Rouault +Date: Fri Sep 8 17:36:29 2023 +0200 + + XRef::reserve(): fix use-after-free and integer overflow on large + XRref /Size + + This fixes two issues: + + - when using greallocn(), and the reallocation failed, the previous + 'entries' array was unexpectedly freed, causing later use-after-free + + ``` + $ (ulimit -v 1000000; valgrind ./utils/pdftoppm -png test.pdf) + Out of memory + ==1251090== Invalid read of size 4 + ==1251090== at 0x49F685B: Object::free() (poppler/Object.cc:115) + ==1251090== by 0x4A339BB: ~Object (poppler/Object.h:171) + ==1251090== by 0x4A339BB: XRef::resize(int) (poppler/XRef.cc:459) + ==1251090== by 0x4A32E20: XRef::constructXRef(bool*, bool) + (poppler/XRef.cc:877) + ==1251090== by 0x4A32CD4: XRef::XRef(BaseStream*, long + long, long long, bool*, bool, std::function const&) + (poppler/XRef.cc:318) + ==1251090== by 0x4A00531: PDFDoc::setup(std::optional + const&, std::optional const&, std::function + const&) (poppler/PDFDoc.cc:247) + ==1251090== by 0x4A002DB: PDFDoc::PDFDoc(std::unique_ptr >&&, std::optional const&, + std::optional const&, void*, std::function const&) + (poppler/PDFDoc.cc:161) + ==1251090== by 0x49F49EA: LocalPDFDocBuilder::buildPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/LocalPDFDocBuilder.cc:0) + ==1251090== by 0x4A1B1E5: PDFDocFactory::createPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/PDFDocFactory.cc:62) + ==1251090== by 0x4035CA: main (utils/pdftoppm.cc:503) + ==1251090== Address 0x6698648 is 24 bytes inside a block of size + 40,960 free'd + ==1251090== at 0x483CA3F: free (in + /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) + ==1251090== by 0x4927B1C: greallocn(void*, int, int, bool, bool) + (goo/gmem.h:0) + ==1251090== by 0x4A33A13: greallocn_checkoverflow (goo/gmem.h:185) + ==1251090== by 0x4A33A13: reserve (poppler/XRef.cc:430) + ==1251090== by 0x4A33A13: XRef::resize(int) (poppler/XRef.cc:446) + ==1251090== by 0x4A32CB5: XRef::XRef(BaseStream*, long + long, long long, bool*, bool, std::function const&) + (poppler/XRef.cc:317) + ==1251090== by 0x4A00531: PDFDoc::setup(std::optional + const&, std::optional const&, std::function + const&) (poppler/PDFDoc.cc:247) + ==1251090== by 0x4A002DB: PDFDoc::PDFDoc(std::unique_ptr >&&, std::optional const&, + std::optional const&, void*, std::function const&) + (poppler/PDFDoc.cc:161) + ==1251090== by 0x49F49EA: LocalPDFDocBuilder::buildPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/LocalPDFDocBuilder.cc:0) + ==1251090== by 0x4A1B1E5: PDFDocFactory::createPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/PDFDocFactory.cc:62) + ==1251090== by 0x4035CA: main (utils/pdftoppm.cc:503) + ==1251090== Block was alloc'd at + ==1251090== at 0x483B7F3: malloc (in + /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) + ==1251090== by 0x4927ACD: grealloc (goo/gmem.h:77) + ==1251090== by 0x4927ACD: greallocn(void*, int, int, bool, bool) + (goo/gmem.h:174) + ==1251090== by 0x4A33A13: greallocn_checkoverflow (goo/gmem.h:185) + ==1251090== by 0x4A33A13: reserve (poppler/XRef.cc:430) + ==1251090== by 0x4A33A13: XRef::resize(int) (poppler/XRef.cc:446) + ==1251090== by 0x4A3344E: XRef::constructXRef(bool*, bool) + (poppler/XRef.cc:979) + ==1251090== by 0x4A32BF9: XRef::XRef(BaseStream*, long + long, long long, bool*, bool, std::function const&) + (poppler/XRef.cc:304) + ==1251090== by 0x4A00531: PDFDoc::setup(std::optional + const&, std::optional const&, std::function + const&) (poppler/PDFDoc.cc:247) + ==1251090== by 0x4A002DB: PDFDoc::PDFDoc(std::unique_ptr >&&, std::optional const&, + std::optional const&, void*, std::function const&) + (poppler/PDFDoc.cc:161) + ==1251090== by 0x49F49EA: LocalPDFDocBuilder::buildPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/LocalPDFDocBuilder.cc:0) + ==1251090== by 0x4A1B1E5: PDFDocFactory::createPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/PDFDocFactory.cc:62) + ==1251090== by 0x4035CA: main (utils/pdftoppm.cc:503) + ``` + + - the logic to exponentially resize the capacity of the array was + relying on undefined behaviour of overflow of int. Change that to + explictely test the value of the capacity before multiplying by 2. + + poppler/XRef.cc | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +commit c44c25ea36a0bf33b6ba46d16ab922dcc13f591e +Author: Albert Astals Cid +Date: Mon Sep 11 23:37:06 2023 +0200 + + PDFDoc::savePageAs: Return cleanly if page is not a dict + + poppler/PDFDoc.cc | 8 ++++++++ + 1 file changed, 8 insertions(+) + +commit 1223c1eb42c62d1587d929b8c72ad8513f0e4b7d +Author: Albert Astals Cid +Date: Tue Sep 12 22:45:48 2023 +0200 + + CI: unbreak mingw + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit cc2fd7c39f1fddd233336099c031bff6bc0d9828 +Author: Sune Vuorela +Date: Thu Sep 7 16:06:40 2023 +0200 + + More supported gnupg releases + + The upcoming 2.2.42 version will have the bugfixes backported for pdf + signatures to work. + + poppler/GPGMECryptoSignBackend.cc | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +commit 3f2e643c4e1c4295c7521681216a90bb02518748 +Author: Albert Astals Cid +Date: Wed Sep 6 22:15:18 2023 +0200 + + FormField::getFullyQualifiedName: Fix infinite loop on malformed files + + poppler/Form.cc | 52 +++++++++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 23 deletions(-) + +commit d253013a0ce0dd0cf79070dd718377b3b3afc7ce +Author: Albert Astals Cid +Date: Wed Sep 6 21:54:30 2023 +0200 + + Update (C) + + utils/pdfsig.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 4ba2441bcbf0b4a7b2aaa080ce3a9a1d04d80e64 +Author: Oliver Sander +Date: Sat Jun 17 09:26:15 2023 +0200 + + Let method utf8ToUtf16WithBom return a std::string object + + Rather than a std::unique_ptr to a GooString. This avoids a heap + allocation. + + glib/poppler-document.cc | 4 ++-- + poppler/Lexer.cc | 5 +++-- + poppler/UTF.cc | 10 +++++----- + poppler/UTF.h | 9 ++++++--- + qt5/tests/check_utf_conversion.cpp | 4 ++-- + qt6/tests/check_utf_conversion.cpp | 4 ++-- + utils/pdfattach.cc | 10 +++++----- + utils/pdfsig.cc | 4 ++-- + 8 files changed, 27 insertions(+), 23 deletions(-) + +commit 6e2c0fe0434cfadc3d161e7cd348212f644b725e +Author: Albert Astals Cid +Date: Tue Sep 5 23:15:14 2023 +0200 + + poppler 23.09.0 + + CMakeLists.txt | 4 ++-- + NEWS | 20 ++++++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 27 insertions(+), 7 deletions(-) + +commit 73985c62006b7faa0fd46a1bc83e578809da7ae2 +Author: Albert Astals Cid +Date: Tue Sep 5 20:21:55 2023 +0200 + + Update (C) + + qt5/src/poppler-ps-converter.cc | 1 + + qt5/src/poppler-qt5.h | 1 + + qt6/src/poppler-ps-converter.cc | 1 + + qt6/src/poppler-qt6.h | 1 + + 4 files changed, 4 insertions(+) + +commit 309cee09f2f199dd70d066fad63a9b730881d151 +Author: Albert Astals Cid +Date: Tue Sep 5 09:49:31 2023 +0200 + + CI: Use non deprecated variable + + Hopefully this fixes the problem with wrong caches being picked up + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 00b7f3471ec8d9e40d8f713b30cd8378fe7cf06f +Author: Albert Astals Cid +Date: Tue Sep 5 00:47:52 2023 +0200 + + PDFDoc::savePageAs: Return cleanly if Catalog Pages is not a dict + + poppler/PDFDoc.cc | 8 ++++++++ + 1 file changed, 8 insertions(+) + +commit 27acee72fd3da6ce3d2027f7758d22b98022ccd4 +Author: Kevin Ottens +Date: Wed Aug 30 15:56:06 2023 +0200 + + Allow to force a rasterized overprint preview during PS conversion + + qt5/src/poppler-ps-converter.cc | 14 ++++++++++++++ + qt5/src/poppler-qt5.h | 13 ++++++++++++- + qt6/src/poppler-ps-converter.cc | 14 ++++++++++++++ + qt6/src/poppler-qt6.h | 13 ++++++++++++- + 4 files changed, 52 insertions(+), 2 deletions(-) + +commit 785a87baa33059d6293c8764e863b1bd7f9ffaf9 +Author: Albert Astals Cid +Date: Tue Sep 5 00:09:42 2023 +0200 + + Update (C) + + goo/GooString.cc | 1 + + poppler/Lexer.cc | 1 + + poppler/NameToUnicodeTable.h | 2 +- + 3 files changed, 3 insertions(+), 1 deletion(-) + +commit e121f3d29f2bdbf44d9e4d67729c1423dbc1db82 +Author: Even Rouault +Date: Wed Aug 30 22:42:05 2023 +0200 + + GooString::appendfv(): emit warning on strings > 2 GB + + goo/GooString.cc | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +commit b8dc27ac07dfea0141211124944b835c35c0d626 +Author: Even Rouault +Date: Mon Aug 28 10:50:04 2023 +0200 + + GooString::appendfv(): avoid int overflow on strings > 2 GB + + goo/GooString.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 4202ea8077e16ef62d4955086e80e4347e3f61c4 +Author: Even Rouault +Date: Mon Aug 28 10:45:04 2023 +0200 + + Lexer: limit max name token length to 1 MB. + + Currently there is no hard limit to the maximum length of a name, + which + may cause the ``n`` variable to overflow INT_MAX (mostly unnoticed + unless building with -ftrapv), but later when drawing a page + using such + invalid huge name, ``len = strlen(str)`` at line 417 of GooString.cc, + invoked by ``error(errSyntaxError, -1, "XObject '{0:s}' is unknown", + name)`` in Gfx.cc::334, overflows to a negative value, which causes an + invalid length exception in ``append(str, len)`` at GooString.cc:445 + + Thus we limit the maximum length of a name to 1 MB which is way beyond + what the spec allows. + + poppler/Lexer.cc | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 38692ffde1dd8f1321f10ed3d962283cd96662e0 +Author: Vincent Lefevre +Date: Wed Aug 30 14:17:07 2023 +0200 + + Add ToUnicode support for similarequal + + For \simeq, TeX generates /similarequal instead of Adobe's + /asymptoticallyequal; so similarequal needs to be supported too. + + In TeX Live 2023: + texmf-dist/fonts/map/glyphlist/glyphlist.txt (Adobe Glyph List) + contains + asymptoticallyequal;2243 + but texmf-dist/fonts/map/glyphlist/texglyphlist.txt (Extensions to the + Adobe Glyph List for TeX fonts and encodings) contains + similarequal;2243 + As a consequence, texmf-dist/tex/generic/pdftex/glyphtounicode.tex + contains both + \pdfglyphtounicode{asymptoticallyequal}{2243} + \pdfglyphtounicode{similarequal}{2243} + + NameToUnicodeTable.h already has + { 0x2243, "asymptoticallyequal" } + so one just needs to add the missing + { 0x2243, "similarequal" } + + poppler/NameToUnicodeTable.h | 1 + + 1 file changed, 1 insertion(+) + +commit 67bd938e9c6b75bd34e0231fd50b567b118e69af +Author: Albert Astals Cid +Date: Wed Aug 30 11:11:45 2023 +0200 + + Add (C) + + poppler/Annot.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 3fd5a6f4dfc5c6388d896ac72ffa9584c753ef22 +Author: Sune Vuorela +Date: Thu Aug 10 16:36:03 2023 +0200 + + Fix digital signatures for NeedAppearance=true + + Always write out the appearance streams for signatures. Several pdf + readers can't create it + - firefox pdf viewer + - ghostscript + - ourselves + Also, annotations without appearance streams are deprecated in pdf2.0. + + Also, if appearance streams exists for a digital signature, don't + regenerate it, as that results is an empty visual representation, + and it + might also fiddle with the signature valudation + + It is not completely clear from the standard if this is 100% + conforming + behavior, but NeedAppearance should be set to true if some annotatinos + are without appearance streams; it is not really described what should + happen if NeedAppearances is true and some annotations are with + appearance streams. + + poppler/Annot.cc | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +commit 2368e099c9cd36d8156a2d188d6cab1883410ff7 +Author: Albert Astals Cid +Date: Wed Aug 23 16:32:08 2023 +0200 + + Update (C) + + poppler/GlobalParams.cc | 1 + + poppler/GlobalParams.h | 1 + + qt5/src/poppler-private.cc | 1 + + qt6/src/poppler-private.cc | 1 + + 4 files changed, 4 insertions(+) + +commit bdaa042978b0bd3e6bcbc1879263597837336715 +Author: Shivodit Gill +Date: Wed Aug 23 12:34:09 2023 +0000 + + Add Android-specific font matching functionality to Poppler + + CMakeLists.txt | 16 +++-- + config.h.cmake | 3 + + poppler/GlobalParams.cc | 161 + ++++++++++++++++++++++++++++++++++++++++++++- + poppler/GlobalParams.h | 4 +- + qt5/src/Mainpage.dox | 19 ++++++ + qt5/src/poppler-private.cc | 33 +++++++++- + qt6/src/Mainpage.dox | 20 ++++++ + qt6/src/poppler-private.cc | 32 +++++++++ + 8 files changed, 281 insertions(+), 7 deletions(-) + +commit 7da36fe953d67c9a14a42bfda28d44cd3bf1880c +Author: Albert Astals Cid +Date: Wed Aug 9 18:59:48 2023 +0200 + + Revert "CI: fedora mingw qt6 is borked again :/" + + This reverts commit 9f1885168bd380583390a805b580af1604a3990b. + + .gitlab-ci.yml | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +commit 7dedced88e354625ef3f4dc09c9732a4d91cf5d7 +Author: Sune Vuorela +Date: Wed Aug 2 22:39:27 2023 +0000 + + Provide the key location for certificates you can sign with + + poppler/CertificateInfo.cc | 11 ++++++++++- + poppler/CertificateInfo.h | 19 +++++++++++++++++++ + poppler/GPGMECryptoSignBackend.cc | 7 +++++++ + qt5/src/poppler-form.cc | 23 +++++++++++++++++++++++ + qt5/src/poppler-form.h | 25 +++++++++++++++++++++++++ + qt6/src/poppler-form.cc | 23 +++++++++++++++++++++++ + qt6/src/poppler-form.h | 25 +++++++++++++++++++++++++ + utils/pdfsig.cc | 18 +++++++++++++++++- + 8 files changed, 149 insertions(+), 2 deletions(-) + +commit 1f1823045d71cc2f00ef43320cd181adf68a9d9f +Author: Sune Vuorela +Date: Tue Jul 18 10:07:50 2023 +0200 + + Don't look up same glyph multiple times + + When iterating over the unicode codepoints, no need to check + the fonts + if they still contains the same glyph as we already checked. + + poppler/Form.cc | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit 6e96ac9d2f5003e27bb2772ea71d76f0d2c30ebe +Author: Albert Astals Cid +Date: Tue Aug 1 23:51:42 2023 +0200 + + poppler 23.08.0 + + CMakeLists.txt | 2 +- + NEWS | 16 ++++++++++++++++ + cpp/Doxyfile | 2 +- + glib/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 6 files changed, 21 insertions(+), 5 deletions(-) + +commit 9f1885168bd380583390a805b580af1604a3990b +Author: Albert Astals Cid +Date: Tue Aug 1 20:14:39 2023 +0200 + + CI: fedora mingw qt6 is borked again :/ + + .gitlab-ci.yml | 12 ------------ + 1 file changed, 12 deletions(-) + +commit 9c98812e8c68401c201719ce0c7a5ea67cd00f5d +Author: Marek Kasik +Date: Thu Jul 27 17:03:41 2023 +0200 + + glib: Add new members to PopplerCertificateInfo + + These new members of the structure can be used to show more detailed + info about + who signed the document and more info about issuer of the certificate. + + Add PopplerCertificateInfo to PopplerSignatureInfo to be able + to get certificate info of the signature. + + glib/poppler-form-field.cc | 214 + ++++++++++++++++++++++++++++++++++-- + glib/poppler-form-field.h | 18 ++- + glib/reference/poppler-sections.txt | 10 ++ + glib/reference/poppler.types | 1 + + 4 files changed, 230 insertions(+), 13 deletions(-) + +commit eb3adcd80ec3ac2d7e2c5e56aec49f9e8aab0ba2 +Author: Albert Astals Cid +Date: Sun Jul 23 16:11:33 2023 +0200 + + Update (C) + + poppler/Catalog.cc | 1 + + 1 file changed, 1 insertion(+) + +commit d8d63630427ca39da995e148fc1a80010c1804f9 +Author: Ilaï Deutel +Date: Wed Jul 19 19:07:54 2023 -0400 + + Fix float-cast-overflow error in Catalog + + This error was triggered when running the page_label_fuzzer using + https://github.com/mozilla/pdf.js/blob/master/test/pdfs/poppler-67295-0.pdf + as an input. + + The fix avoids extraneous casts in Catalog.cc: + + - Before: + - int -> double -> int + - long long -> double -> int + - double -> int + - After: + - int (no cast) + - long long -> int + - double -> int + + poppler/Catalog.cc | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +commit 0e57662f92847931e3a2ed6cee32f552b996a100 +Author: Sune Vuorela +Date: Thu Jul 6 12:06:36 2023 +0200 + + Mark pdf file argument as required; make documentation match code + + utils/pdftotext.1 | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 823de0858dff673f4ecdbad3386a702b5e0ec3b8 +Author: Sune Vuorela +Date: Thu Jul 6 09:07:28 2023 +0200 + + Version symbols in poppler core + + This prevents crashes and collisions in the case where two poppler + core + libraries are in the same process. + + The likely case is if an application is linked to both poppler core + and a stable frontend, and poppler is updated and the application + is not yet recompiled against the newer poppler core, then the newer + frontend pulls in newer poppler core while the application pulls + in the + older poppler core and that leads to crashes. In general, this can be + fixed by versioning the symbols to prevent mix and match of symbols. + + Patch by Andreas Metzler + + CMakeLists.txt | 17 +++++++++++++++-- + poppler/libpoppler.map.in | 4 ++++ + 2 files changed, 19 insertions(+), 2 deletions(-) + +commit fb198157f32f15e17595730ab82d03d81cdb8010 +Author: Albert Astals Cid +Date: Sun Jul 16 16:31:15 2023 +0200 + + Update (C) + + poppler/XRef.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 22c68e52fd8f761cd1b3f38253fcae00055b02f9 +Author: Ilaï Deutel +Date: Thu Jul 13 14:03:56 2023 -0400 + + Fix use-of-uninitialized-value in XRef + + Attribute `keyLength` of `XRef` is not initialized on object + creation, and is subsequently used, for instance in `XRef::fetch` + ([poppler/XRef.cc:1214](https://gitlab.freedesktop.org/poppler/poppler/-/blob/e0148dbc9a0189d1ee982a1b3e763930e086b919/poppler/XRef.cc#L1214)). + This was flagged by running `MemorySanitizer`. + + poppler/XRef.cc | 1 + + 1 file changed, 1 insertion(+) + +commit e0148dbc9a0189d1ee982a1b3e763930e086b919 +Author: Thomas Freitag +Date: Wed Jul 12 14:42:25 2023 +0000 + + avoid bogus memory allocation size in doTilingPatternFill + + poppler/SplashOutputDev.cc | 9 +++++++++ + 1 file changed, 9 insertions(+) + +commit 4ff8402775aeae27cecb9b372eda7909a3524b20 +Author: Albert Astals Cid +Date: Mon Jul 10 20:53:31 2023 +0200 + + Update (C) + + poppler/GfxState.cc | 2 +- + poppler/SplashOutputDev.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit 5726aff3cd2b5a2f84200d75ff8b875f8827c6c5 +Author: Thomas Freitag +Date: Mon Jul 10 09:08:01 2023 +0000 + + GWG 19.2 - DeviceN Overprint (White): + + poppler/GfxState.cc | 2 +- + poppler/SplashOutputDev.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit ee2af17e916e6310ab25b92a638df380b18a24ee +Author: Sune Vuorela +Date: Wed Jul 5 17:30:46 2023 +0200 + + Remove unused constructor parameter + + poppler/GPGMECryptoSignBackend.cc | 6 +++--- + poppler/GPGMECryptoSignBackend.h | 2 +- + 2 files changed, 4 insertions(+), 4 deletions(-) + +commit e54d8611d979f0034f81f1c51c2dfcd14200038d +Author: Jan-Michael Brummer +Date: Tue Jul 4 10:53:15 2023 +0200 + + Improve poppler_get_available_signing_certificates + + Remove #ifdef and error handling + + Fixes: https://gitlab.freedesktop.org/poppler/poppler/-/issues/1412 + + glib/poppler-form-field.cc | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +commit 639efb3d3b08fb10fb84976cb6db98ea929e9486 +Author: Albert Astals Cid +Date: Mon Jul 3 23:05:08 2023 +0200 + + poppler 23.07.0 + + CMakeLists.txt | 4 ++-- + NEWS | 26 ++++++++++++++++++++++++++ + cpp/Doxyfile | 2 +- + glib/CMakeLists.txt | 4 ++-- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 6 files changed, 33 insertions(+), 7 deletions(-) + +commit 1b1036cf4ef1bfd70f300da3534375d02195fe32 +Author: Albert Astals Cid +Date: Mon Jul 3 21:05:14 2023 +0200 + + Update (C) + + poppler/Gfx.cc | 1 + + 1 file changed, 1 insertion(+) + +commit c40bec5715075c12e7158639c638baa95bfcf2e8 +Author: Anton Thomasson +Date: Mon Jun 19 22:04:38 2023 +0200 + + Don't abort command when restore runs in to a state guard + + Fixes #1395 + + poppler/Gfx.cc | 1 - + 1 file changed, 1 deletion(-) + +commit c46203cb5a9c57ba039658d305d6ed8e450d51e8 +Author: Albert Astals Cid +Date: Mon Jul 3 15:25:27 2023 +0200 + + Add a few new NSS enums to ConvertHashTypeFromNss + + poppler/NSSCryptoSignBackend.cc | 7 +++++++ + 1 file changed, 7 insertions(+) + +commit ed7a7887050dd0095ebca2ec7b511f3131242efa +Author: Sune Vuorela +Date: Mon Jul 3 12:40:56 2023 +0200 + + MSVC still unhappy with unicode bits in unicode test + + qt5/tests/check_utf8document.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit badfea70ce6d81ce7c9ce0a671c80f53d92a4b83 +Author: Sune Vuorela +Date: Fri Jun 30 15:04:42 2023 +0200 + + MSVC unhappy with utf8 in QStringLiteral + + qt5/tests/check_utf8document.cpp | 10 +++++----- + qt6/tests/check_utf8document.cpp | 10 +++++----- + 2 files changed, 10 insertions(+), 10 deletions(-) + +commit fac65b3c791b6a1bb3fa83d122b9fce9a6a93ee7 +Author: Jan-Michael Brummer +Date: Fri Jun 30 11:50:29 2023 +0200 + + Remove g_auto functions + + glib/demo/signature.c | 9 ++++++--- + glib/poppler-document.cc | 3 ++- + 2 files changed, 8 insertions(+), 4 deletions(-) + +commit fa28ba42105b04aef25968e1a31eefeec2c48f4e +Author: Albert Astals Cid +Date: Fri Jun 30 00:03:28 2023 +0200 + + Update (C) + + poppler/GlobalParams.cc | 1 + + 1 file changed, 1 insertion(+) + +commit b4f802c88e0642f0f1271cb4a23cd279e699f38d +Author: Sune Vuorela +Date: Wed Jun 28 15:49:24 2023 +0200 + + Improve message on invalid cid font collection + + Hopefully fixes #1408 + + poppler/GlobalParams.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit bdd922b7caaa965828e0e45a6cde0b1585c9740e +Author: Jan-Michael Brummer +Date: Wed Oct 5 08:20:13 2022 +0200 + + Signatures: Add signing API to glib part + + Rectangle corrections by Marek Kasik + + glib/CMakeLists.txt | 6 + + glib/demo/CMakeLists.txt | 1 + + glib/demo/main.c | 4 +- + glib/demo/signature.c | 437 ++++++++++++++ + glib/demo/signature.h | 31 + + glib/poppler-document.cc | 98 ++++ + glib/poppler-document.h | 5 + + glib/poppler-form-field.cc | 1088 + +++++++++++++++++++++++++++++++++++ + glib/poppler-form-field.h | 114 ++++ + glib/poppler.h | 5 +- + glib/reference/poppler-sections.txt | 51 ++ + glib/reference/poppler.types | 2 + + 12 files changed, 1840 insertions(+), 2 deletions(-) + +commit d1e86894cbb617c64b84fe18cae7294b1cb45eed +Author: Albert Astals Cid +Date: Thu Jun 29 00:35:56 2023 +0200 + + Update (C) + + qt5/src/poppler-page.cc | 1 + + qt6/src/poppler-page.cc | 1 + + 2 files changed, 2 insertions(+) + +commit d9d1550a3e337e041cf2801dfcb0366fcb5b16d0 +Author: Albert Astals Cid +Date: Wed Jun 28 00:47:14 2023 +0200 + + Update (C) + + poppler/PDFDoc.cc | 2 +- + poppler/PSOutputDev.cc | 1 + + poppler/PSOutputDev.h | 1 + + poppler/XRef.cc | 1 + + poppler/XRef.h | 1 + + 5 files changed, 5 insertions(+), 1 deletion(-) + +commit 7d87c7d2c6ca4f814f87329534a17dbf30203313 +Author: Sune Vuorela +Date: Tue Jun 27 22:11:29 2023 +0000 + + Allow for stream compression and compress font streams in forms + + CMakeLists.txt | 21 ++++----------------- + config.h.cmake | 3 --- + poppler/Form.cc | 6 +++--- + poppler/PDFDoc.cc | 28 ++++++++++++++++++---------- + poppler/PSOutputDev.cc | 29 ++++++----------------------- + poppler/PSOutputDev.h | 11 +---------- + poppler/XRef.cc | 13 ++++++++++--- + poppler/XRef.h | 11 +++++++++-- + poppler/poppler-config.h.cmake | 5 ----- + 9 files changed, 51 insertions(+), 76 deletions(-) + +commit 2c403ec533ad9ad821c37e3f80f8f361ce9cdea8 +Author: Albert Astals Cid +Date: Mon Jun 26 00:51:22 2023 +0200 + + Fix crash if CERT_ExtractPublicKey doesn't return a public key + + Fixes KDE Bug #471422 + + poppler/NSSCryptoSignBackend.cc | 40 + ++++++++++++++++++++++------------------ + 1 file changed, 22 insertions(+), 18 deletions(-) + +commit 6ebe45e8dceae11d02c74df47c34f4490a45a15e +Author: Kevin Ottens +Date: Wed Jun 21 15:10:48 2023 +0200 + + Don't crash when overprint preview is enabled with the Qt bindings + + When overprint preview is enabled, the image data row size changes + during conversion to XBGR. The Qt bindings were assuming this row + size was constant which led to badly broken QImages at conversion + time (generating garbage if displayed or even crashing). + + This commit simply add regression tests for the case and gets the + row size after bitmap conversion. + + qt5/src/poppler-page.cc | 8 ++++---- + qt5/tests/CMakeLists.txt | 1 + + qt5/tests/check_overprint.cpp | 41 + +++++++++++++++++++++++++++++++++++++++++ + qt6/src/poppler-page.cc | 8 ++++---- + qt6/tests/CMakeLists.txt | 1 + + qt6/tests/check_overprint.cpp | 38 ++++++++++++++++++++++++++++++++++++++ + 6 files changed, 89 insertions(+), 8 deletions(-) + +commit f0d648c3f0cb69121d0115d468401d58255ad0cf +Author: Albert Astals Cid +Date: Fri Jun 16 11:36:53 2023 +0200 + + Update (C) + + poppler/UTF.cc | 1 + + poppler/UTF.h | 1 + + 2 files changed, 2 insertions(+) + +commit 7b199526e6f7bb5a28e8ddd017e23ae51b85b9f6 +Author: Sune Vuorela +Date: Wed Jun 14 16:13:23 2023 +0200 + + Cryptosign: OCSP and AIA: Document + + Document how the backends interacts with the OCSP and AIA flags, + and ensure the implementation matches. + + poppler/GPGMECryptoSignBackend.cc | 2 +- + qt5/src/poppler-form.h | 12 ++++++++++++ + qt6/src/poppler-form.h | 12 ++++++++++++ + 3 files changed, 25 insertions(+), 1 deletion(-) + +commit b8c55037fcfc0d3910cad5a4e65b3161bd5ea585 +Author: Oliver Sander +Date: Wed Jun 14 14:11:52 2023 +0200 + + Store PSOutPaperSize objects by value + + This simplifies the code, and saves a few heap allocations. + + poppler/PSOutputDev.cc | 35 ++++++++--------------------------- + poppler/PSOutputDev.h | 14 ++++++++++++-- + 2 files changed, 20 insertions(+), 29 deletions(-) + +commit b2ae26814909047b27f30cffaed225b5bb9f96e9 +Author: Oliver Sander +Date: Wed Jun 14 13:23:54 2023 +0200 + + Store PSOutPaperSize::name as std::string + + Rather than as a pointer to a GooString + + Safer and simpler code, and less memory allocations. + + poppler/PSOutputDev.cc | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +commit 9306058027b24b926e98da9651df9d56e5f744b0 +Author: Oliver Sander +Date: Wed Jun 14 11:16:13 2023 +0200 + + Store the paperSizes vector of PSOutputDev as object + + Rather than storing a pointer and allocating the std::vector + on the heap. + + This simplifies the code and saves a few heap allocations. + + poppler/PSOutputDev.cc | 28 +++++++++++----------------- + poppler/PSOutputDev.h | 4 ++-- + 2 files changed, 13 insertions(+), 19 deletions(-) + +commit e885124ab3b071b7fbb2f001e4a9a88b7e758605 +Author: Even Rouault +Date: Thu Jun 15 20:25:24 2023 +0200 + + utf8ToUtf16(): fix out-of-bounds write + + Fixes a regression introduced by recent + 9183da4fcb8d06360ed51f7f1131a14300008735 commit which caused the + following Valgrind error: + + ``` + $ valgrind utils/pdftoppm /tmp/test.pdf + ==3735668== Memcheck, a memory error detector + ==3735668== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward + et al. + ==3735668== Using Valgrind-3.15.0 and LibVEX; rerun with -h for + copyright info + ==3735668== Command: utils/pdftoppm /tmp/test.pdf + ==3735668== + Syntax Warning: May not be a PDF file (continuing anyway) + Syntax Error: Unterminated string + ==3735668== Invalid write of size 2 + ==3735668== at 0x4A3570C: utf8ToUtf16(char const*, unsigned short*, + int, int) (poppler/UTF.cc:353) + ==3735668== by 0x4A3584C: utf8ToUtf16(char const*, int*) + (poppler/UTF.cc:368) + ==3735668== by 0x4A358D4: + utf8ToUtf16WithBom(std::__cxx11::basic_string, std::allocator > const&) + (poppler/UTF.cc:379) + ==3735668== by 0x49F2C97: Lexer::getObj(int) (poppler/Lexer.cc:424) + ==3735668== by 0x4A035C2: Parser::Parser(XRef*, Stream*, bool) + (poppler/Parser.cc:50) + ==3735668== by 0x49F888B: Linearization::Linearization(BaseStream*) + (poppler/Linearization.cc:28) + ==3735668== by 0x4A06D8D: getLinearization (poppler/PDFDoc.cc:648) + ==3735668== by 0x4A06D8D: PDFDoc::isLinearized(bool) + (poppler/PDFDoc.cc:700) + ==3735668== by 0x4A0518D: PDFDoc::getStartXRef(bool) + (poppler/PDFDoc.cc:2003) + ==3735668== by 0x4A04BB8: PDFDoc::setup(std::optional + const&, std::optional const&, std::function + const&) (poppler/PDFDoc.cc:246) + ==3735668== by 0x4A04AAB: PDFDoc::PDFDoc(std::unique_ptr >&&, std::optional const&, + std::optional const&, void*, std::function const&) + (poppler/PDFDoc.cc:160) + ==3735668== by 0x49F93EA: LocalPDFDocBuilder::buildPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/LocalPDFDocBuilder.cc:0) + ==3735668== by 0x4A1FBB5: PDFDocFactory::createPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/PDFDocFactory.cc:62) + ==3735668== Address 0x669cf54 is 0 bytes after a block of size + 148 alloc'd + ==3735668== at 0x483B7F3: malloc (in + /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) + ==3735668== by 0x4A35815: gmalloc (goo/gmem.h:44) + ==3735668== by 0x4A35815: gmallocn (goo/gmem.h:121) + ==3735668== by 0x4A35815: utf8ToUtf16(char const*, int*) + (poppler/UTF.cc:367) + ==3735668== by 0x4A358D4: + utf8ToUtf16WithBom(std::__cxx11::basic_string, std::allocator > const&) + (poppler/UTF.cc:379) + ==3735668== by 0x49F2C97: Lexer::getObj(int) (poppler/Lexer.cc:424) + ==3735668== by 0x4A035C2: Parser::Parser(XRef*, Stream*, bool) + (poppler/Parser.cc:50) + ==3735668== by 0x49F888B: Linearization::Linearization(BaseStream*) + (poppler/Linearization.cc:28) + ==3735668== by 0x4A06D8D: getLinearization (poppler/PDFDoc.cc:648) + ==3735668== by 0x4A06D8D: PDFDoc::isLinearized(bool) + (poppler/PDFDoc.cc:700) + ==3735668== by 0x4A0518D: PDFDoc::getStartXRef(bool) + (poppler/PDFDoc.cc:2003) + ==3735668== by 0x4A04BB8: PDFDoc::setup(std::optional + const&, std::optional const&, std::function + const&) (poppler/PDFDoc.cc:246) + ==3735668== by 0x4A04AAB: PDFDoc::PDFDoc(std::unique_ptr >&&, std::optional const&, + std::optional const&, void*, std::function const&) + (poppler/PDFDoc.cc:160) + ==3735668== by 0x49F93EA: LocalPDFDocBuilder::buildPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/LocalPDFDocBuilder.cc:0) + ==3735668== by 0x4A1FBB5: PDFDocFactory::createPDFDoc(GooString + const&, std::optional const&, std::optional + const&, void*) (poppler/PDFDocFactory.cc:62) + ``` + + Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=59840 + + poppler/UTF.cc | 2 +- + poppler/UTF.h | 2 +- + qt5/tests/check_utf_conversion.cpp | 2 +- + qt6/tests/check_utf_conversion.cpp | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +commit cd99989705824202d157c33d48c998e9818ea2f3 +Author: Sune Vuorela +Date: Mon Jun 12 14:19:18 2023 +0200 + + Revert "Remove poppler mingw CI until it's fixed again" + + This reverts commit 2dcbf58328e5acaafaa73dc2e601e5737c685789. + + .gitlab-ci.yml | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +commit 8eea66b804c2651be4d91eed4b0992cc0c6f3e47 +Author: Albert Astals Cid +Date: Wed Jun 14 22:13:17 2023 +0200 + + PSOutputDev::writePSFmt is GOOSTRING_FORMAT + + poppler/PSOutputDev.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 79ba5720ea8067b43d79c5dc100116cee75ccdbe +Author: Oliver Sander +Date: Wed Jun 14 11:08:42 2023 +0200 + + Remove method Hints::getPageRanges + + It is not used. + + poppler/Hints.cc | 41 ----------------------------------------- + poppler/Hints.h | 1 - + 2 files changed, 42 deletions(-) + +commit 222982849168a8aa0a78cb2a4fa2b903f2f74146 +Author: Albert Astals Cid +Date: Wed Jun 14 00:33:00 2023 +0200 + + Update (C) + + poppler/Lexer.cc | 1 + + poppler/UTF.cc | 1 + + poppler/UTF.h | 1 + + 3 files changed, 3 insertions(+) + +commit 9183da4fcb8d06360ed51f7f1131a14300008735 +Author: Sune Vuorela +Date: Tue Jun 13 22:24:24 2023 +0000 + + Fix reading of utf8-with-bom files + + poppler/Lexer.cc | 27 ++++++++++++------- + poppler/UTF.cc | 3 +++ + poppler/UTF.h | 11 ++++++++ + qt5/tests/CMakeLists.txt | 3 ++- + qt5/tests/check_utf8document.cpp | 57 + ++++++++++++++++++++++++++++++++++++++++ + qt6/tests/CMakeLists.txt | 1 + + qt6/tests/check_utf8document.cpp | 57 + ++++++++++++++++++++++++++++++++++++++++ + 7 files changed, 148 insertions(+), 11 deletions(-) + +commit 7cf6cd7b9a0fc496738c245421eea54ec669448d +Author: Sune Vuorela +Date: Mon Jun 12 12:22:13 2023 +0200 + + Describe signature dump format in manual page + + fixes #215 + + utils/pdfsig.1 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 19679f2873369117204c3e555967a5c9ed9187d6 +Author: Sune Vuorela +Date: Fri Jun 9 16:30:47 2023 +0200 + + Fix build without NSS + + Happened as a mismerge during !1394 + + utils/pdfsig.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit f67c0187430b04b8f575686357a1669142e2949e +Author: Sune Vuorela +Date: Fri Jun 9 16:27:37 2023 +0200 + + Fix 32bit build. + + Resolves !1402. + Thanks, Thomas + + poppler/GPGMECryptoSignBackend.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 8d4764b498d4f9798e9aaedbce5e442d382acb0a +Author: Sune Vuorela +Date: Tue Jun 6 16:02:25 2023 +0000 + + pdfsig: Allow show and select backend + + utils/CMakeLists.txt | 6 ++++-- + utils/pdfsig.1 | 8 +++++++- + utils/pdfsig.cc | 52 + +++++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 60 insertions(+), 6 deletions(-) + +commit def45c82b36ec393fbaf16d9873db23bc9659b80 +Author: Sune Vuorela +Date: Mon Jun 5 12:36:44 2023 +0200 + + Don't fail signature basics tests if backend is not configured + + qt5/tests/check_signature_basics.cpp | 22 +++++++++++++++++----- + qt6/tests/check_signature_basics.cpp | 22 +++++++++++++++++----- + 2 files changed, 34 insertions(+), 10 deletions(-) + +commit f0373f62df9cb6b7bff29150344fddfe1e8361c5 +Author: Albert Astals Cid +Date: Mon Jun 5 12:12:40 2023 +0200 + + poppler 23.06.0 + + CMakeLists.txt | 4 ++-- + NEWS | 23 +++++++++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/CMakeLists.txt | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/CMakeLists.txt | 2 +- + qt6/src/Doxyfile | 2 +- + 7 files changed, 30 insertions(+), 7 deletions(-) + +commit fa00b3fbbd62c5b6b5383d9997a9d5c30827b386 +Author: Sune Vuorela +Date: Mon Jun 5 11:09:09 2023 +0200 + + Fix cornercase crash in weird pdfsig setups + + This fixes a potential crash in setups where at least one + cryptographic + signature backend is compiled in, but none is functional. + + utils/pdfsig.cc | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +commit c002bee1f2010d13c85a46e6816920ecdccb3015 +Author: Sune Vuorela +Date: Thu May 25 13:21:08 2023 +0200 + + Store embedded fonts widths table more effective + + Some non-scientific experiments: + A single 8k pdf document with okular adding a simple typewriter + annotation with content "aoeu" in approximately the same place grows + without this patch to 894k. With this patch, it grows to 649k. + + For comparison, if one doesn't embed the widths content at all, + the same + process grew to 638k + + While this isn't ground breaking gains, it is still some improvement. + + poppler/CIDFontsWidthsBuilder.h | 190 + ++++++++++++++++++++++++++++++ + poppler/Form.cc | 36 +++++- + qt5/tests/CMakeLists.txt | 1 + + qt5/tests/check_cidfontswidthsbuilder.cpp | 100 ++++++++++++++++ + qt6/tests/CMakeLists.txt | 1 + + qt6/tests/check_cidfontswidthsbuilder.cpp | 100 ++++++++++++++++ + 6 files changed, 423 insertions(+), 5 deletions(-) + +commit 2dcbf58328e5acaafaa73dc2e601e5737c685789 +Author: Albert Astals Cid +Date: Mon Jun 5 00:56:51 2023 +0200 + + Remove poppler mingw CI until it's fixed again + + https://bugzilla.redhat.com/show_bug.cgi?id=2212050 + + .gitlab-ci.yml | 12 ------------ + 1 file changed, 12 deletions(-) + +commit 981210b9dcc47ce2209ae7091cf6df87c958b6b2 +Author: Sune Vuorela +Date: Tue May 30 22:06:20 2023 +0000 + + Rename NSS CryptoSign backend classes and files in line with GPG + backend. + + CMakeLists.txt | 2 +- + poppler/CryptoSignBackend.cc | 2 +- + ...SignatureHandler.cc => NSSCryptoSignBackend.cc} | 58 + +++++++++++++--------- + .../{SignatureHandler.h => NSSCryptoSignBackend.h} | 32 ++++++------ + qt5/src/poppler-form.cc | 8 +-- + qt6/src/poppler-form.cc | 8 +-- + utils/pdfsig.cc | 8 +-- + 7 files changed, 63 insertions(+), 55 deletions(-) + +commit c6dd11d89a08a6e4ab93d31b6d5d89a9153d9a91 +Author: Albert Astals Cid +Date: Sun May 28 01:00:51 2023 +0200 + + Update (C) + + poppler/GfxFont.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 333b04802a3a9dbdb0d0e0caec553fd1037c4c92 +Author: Khaled Hosny +Date: Mon May 8 18:35:10 2023 +0300 + + Try harder to get Type 3 font name + + There is no BaseFont in Type 3 fonts, so we first try + fontDescriptor’s + FontName, but fontDescriptor is optional so we then try the Name key. + + Fixes #1396 + + poppler/GfxFont.cc | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +commit 82fc01784dcfc3e22ee0053e4ecfa9b500119b19 +Author: Albert Astals Cid +Date: Thu May 25 12:25:57 2023 +0200 + + Update (C) + + qt5/src/poppler-form.h | 1 + + qt6/src/poppler-form.h | 1 + + 2 files changed, 2 insertions(+) + +commit 83630a0ecf3db671c820b80f584e45ab9927c3fe +Author: Sune Vuorela +Date: Wed May 24 23:04:24 2023 +0000 + + Qt frontend code for selecting cryptosign backend + + qt5/src/poppler-form.cc | 88 + ++++++++++++++++++++++++++++++++++++++++++++++++ + qt5/src/poppler-form.h | 55 +++++++++++++++++++++++++++++- + qt6/src/poppler-form.cc | 89 + +++++++++++++++++++++++++++++++++++++++++++++++++ + qt6/src/poppler-form.h | 54 +++++++++++++++++++++++++++++- + 4 files changed, 284 insertions(+), 2 deletions(-) + +commit a2b249f8a5bc6f8fe23d02f6e04cbfd132a46629 +Author: Albert Astals Cid +Date: Tue May 23 00:07:23 2023 +0200 + + Update (C) + + poppler/CairoFontEngine.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 6f7c8d24aa699327917772fb04b7cb52ab5803f5 +Author: Ilia Kats +Date: Mon May 22 11:48:04 2023 +0200 + + fix segfault in _free_type3_font_info + + The destructor of Gfx calls methods of outputDev, so it needs to be + deleted first + + poppler/CairoFontEngine.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 33672ca1b6670f7378e24f6d475438f7f5d86b05 +Author: Sune Vuorela +Date: Mon May 22 19:53:08 2023 +0000 + + Fix crash with weird hashing used for signatures + + poppler/SignatureHandler.cc | 15 ++++++++++++--- + poppler/SignatureHandler.h | 7 ++++++- + 2 files changed, 18 insertions(+), 4 deletions(-) + +commit 4efd2f9f8175cb5d448809b56f67524df6e220aa +Author: Sune Vuorela +Date: Tue Apr 11 13:39:36 2023 +0200 + + Cryptosign backend using gpgme (gpgsm) for all your signature needs + + CMakeLists.txt | 9 +- + config.h.cmake | 3 + + poppler/CryptoSignBackend.cc | 18 ++ + poppler/CryptoSignBackend.h | 3 +- + poppler/GPGMECryptoSignBackend.cc | 387 + +++++++++++++++++++++++++++++++++++ + poppler/GPGMECryptoSignBackend.h | 57 ++++++ + qt5/tests/check_signature_basics.cpp | 52 ++++- + qt6/tests/check_signature_basics.cpp | 50 ++++- + 8 files changed, 557 insertions(+), 22 deletions(-) + +commit 9065bd7d5fb9f9554235eafe8a67d0f67ea25faf +Author: Sune Vuorela +Date: Mon May 1 15:40:39 2023 +0200 + + Set certificate info a bit earlier; that way we also get it if + signature validation fails + + poppler/Form.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit 266a2fd0a8cce584e0ffad0bc06670ed7136fc19 +Author: Sune Vuorela +Date: Mon Mar 27 11:11:24 2023 +0200 + + Add function to get specific element from the DN parse result + + poppler/DistinguishedNameParser.h | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +commit 395a5b7e5a8efa049a0eb3a4bbff41737aaf5fc1 +Author: Sune Vuorela +Date: Fri Mar 10 13:00:09 2023 +0100 + + Parser for Distinguished Names in certificates + + Not all crypto libraries exposes the fields parsed, so we wil need our + own. + + Derived from KDE's libkleo, but improved and has added test coverage + + poppler/DistinguishedNameParser.h | 311 + ++++++++++++++++++++++++++ + qt5/tests/CMakeLists.txt | 1 + + qt5/tests/check_distinguished_name_parser.cpp | 165 ++++++++++++++ + qt6/tests/CMakeLists.txt | 1 + + qt6/tests/check_distinguished_name_parser.cpp | 161 +++++++++++++ + 5 files changed, 639 insertions(+) + +commit 720fcc1f1b7c2d13894ce4dfee4f68a40384d91b +Author: Sune Vuorela +Date: Wed Mar 22 19:22:40 2023 +0100 + + Let's try get in a bit of gnupg + + A script to build a gnupg2.4.1 + A gpgme that is hardcoded to use the above gnupg + And enough CMake to see we can find it. + + .gitlab-ci.yml | 4 +++- + CMakeLists.txt | 10 ++++++++-- + do-the-gnupg-2.4-dance.sh | 51 + +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 62 insertions(+), 3 deletions(-) + +commit f07d64831252a9cb7775d12e5ce9b663aaae63d5 +Author: Albert Astals Cid +Date: Sat May 13 13:16:29 2023 +0200 + + symbol.ttf is not a good Symbol font + + Add the URW++ font names for a proper Symbol replacement + + poppler/GlobalParamsWin.cc | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +commit 591235c8b6c65a2eee88991b9ae73490fd9afdfe +Author: Albert Astals Cid +Date: Wed May 17 22:42:05 2023 +0200 + + OutlineItem::open: Fix crash on malformed files + + Fixes #1399 + + poppler/Outline.cc | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +commit db6d86238a86116f4ef809d6fd19ed859c36b5c1 +Author: Sune Vuorela +Date: Tue May 16 15:29:04 2023 +0200 + + Lookup with same rules as inserting them + + Inserting fonts happens without a trailing space if font style + is empty. + Apply teh same rules when lookin up. + + poppler/Form.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 6b70f3fc046cc0107ca3a1da0faa33e7bbb50255 +Author: Albert Astals Cid +Date: Tue May 16 23:12:57 2023 +0200 + + Update (C) + + goo/JpegWriter.cc | 1 + + poppler/ImageEmbeddingUtils.cc | 1 + + 2 files changed, 2 insertions(+) + +commit 2986f06c7cc9d64a506ebe861b8bf38f73386e86 +Author: Jordan Abrahams-Whitehead +Date: Tue May 16 18:52:19 2023 +0000 + + Add missing #include prior to jpeglib.h + + Fixes #1398 + + goo/JpegWriter.cc | 2 +- + poppler/ImageEmbeddingUtils.cc | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +commit 26199bd53fbe7a0d5b00f8586e66d6702e324e5e +Author: Albert Astals Cid +Date: Fri May 5 00:29:40 2023 +0200 + + Look for fonts in both windows font dir and poppler fonts dir + + poppler/GlobalParamsWin.cc | 201 + +++++++++++++++++++++++---------------------- + 1 file changed, 104 insertions(+), 97 deletions(-) + +commit 8aaf78b06373e2a641e565c3b9da31ac3a25b7d8 +Author: Sune Vuorela +Date: Fri May 12 21:12:43 2023 +0200 + + Skip font lookup for nonprintable characters + + This is at least one cause for + https://bugs.kde.org/show_bug.cgi?id=469664 + + poppler/Form.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 6fd2c2e6f50b6b8f47c554c8e7e3076cd81a7554 +Author: Albert Astals Cid +Date: Fri May 12 16:22:24 2023 +0200 + + Update (C) + + qt5/src/poppler-embeddedfile.cc | 1 + + qt6/src/poppler-embeddedfile.cc | 1 + + 2 files changed, 2 insertions(+) + +commit beb63c33457ed41033fb071eb25bb61e393280f6 +Author: Andre Heinecke +Date: Fri May 5 15:09:00 2023 +0200 + + Fix crash when signing existing signature + + On windows, helvetica is not available and that makes poppler + crash, so + fix crash and add fallback to arial. + + This has already been implemented for "new" signatures, both the + fallback and the crash in + 09fdfecea3a13d30b5c52e1258d17549739d9ea8 and + a563801e1a6be5e70645472d82f4ba8534b454d1 + + Refactor the fallback code to be reusable and use the same crash + guard. + + poppler/Form.cc | 25 +++++++++++++++++++++++-- + poppler/Form.h | 4 ++++ + poppler/PDFDoc.cc | 21 +-------------------- + 3 files changed, 28 insertions(+), 22 deletions(-) + +commit aefd09f9eb69b91ae2cae0351280aaa66742dd84 +Author: Sune Vuorela +Date: Thu May 11 16:43:41 2023 +0200 + + Convert embedded files to bytearray a bit smarter + + The current behavior also triggers a runtime warning per byte: + "Using QByteRef with an index pointing outside the valid range of + a QByteArray. The corresponding behavior is deprecated, and will + be changed in a future version of Qt." + + This will keep an extra copy of the data around during convertion, + but that's probably okay. + + qt5/src/poppler-embeddedfile.cc | 11 ++--------- + qt6/src/poppler-embeddedfile.cc | 14 ++------------ + 2 files changed, 4 insertions(+), 21 deletions(-) + +commit 47856547b9b56c037049e48453f9ae65aa266a26 +Author: Albert Astals Cid +Date: Fri May 5 00:22:18 2023 +0200 + + GooString -> std::string + + poppler/GlobalParamsWin.cc | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +commit 1e4488b2b44a56b35dee8e0bf9e33453523eb2ed +Author: Albert Astals Cid +Date: Thu May 4 00:21:46 2023 +0200 + + FontInfo: Make it return proper information about font substitution + + GfxFont::locateFont is what the output devices use, which is more + complex than just findSystemFontFile so use that + + poppler/FontInfo.cc | 8 +++----- + poppler/GfxFont.cc | 6 +++--- + poppler/GfxFont.h | 3 ++- + poppler/GlobalParams.cc | 8 ++++---- + poppler/GlobalParams.h | 2 +- + 5 files changed, 13 insertions(+), 14 deletions(-) + +commit 6a566e417b7c261252c8d3d83dd9b1c366f83194 +Author: Albert Astals Cid +Date: Thu May 4 14:05:45 2023 +0200 + + Fix memory leak when looking for fonts in Windows + + poppler/GlobalParams.cc | 6 +++--- + poppler/GlobalParams.h | 4 ++-- + poppler/GlobalParamsWin.cc | 21 +++++++++------------ + 3 files changed, 14 insertions(+), 17 deletions(-) + +commit adf710eecf475dd7c64d7b904f7414ec3098491f +Author: Albert Astals Cid +Date: Tue May 2 21:43:28 2023 +0200 + + poppler 23.05.0 + + CMakeLists.txt | 4 ++-- + NEWS | 8 ++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 13 insertions(+), 5 deletions(-) + +commit 91505b388ccb1902f86b7d9ec5baf22e879200b6 +Author: Albert Astals Cid +Date: Tue May 2 21:33:41 2023 +0200 + + Update (C) + + poppler/Form.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 62f2eb80fb2a4d4c656e7583584aa73fbc1de511 +Author: Albert Astals Cid +Date: Sun Apr 30 11:31:43 2023 +0200 + + Form::ensureFontsForAllCharacters: Create resources if there's not one + + Fixes #1383 + + poppler/Form.cc | 21 ++++++++++++++++----- + poppler/Form.h | 10 ++++++---- + 2 files changed, 22 insertions(+), 9 deletions(-) + +commit 0864da5b1063c3ddba7e68a567cc56ed3474f8b2 +Author: Albert Astals Cid +Date: Sun Apr 30 10:28:40 2023 +0200 + + Don't include an empty space at the end of the font name if there's + no style + + poppler/Form.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2b437604157a34546f7340cc383d7cfaa292ede5 +Author: Albert Astals Cid +Date: Sun Apr 30 10:26:36 2023 +0200 + + Do not hardcode Noto Sans as FontName + + poppler/Form.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit c5bc25a0094b3fd10e8eea5296a006a4f530a771 +Author: Nicolas Fella +Date: Fri Apr 28 18:51:59 2023 +0200 + + Set SigFlags when signing unsigned signature + + When creating a new signature we change SigFlags to indicate that + there are now signatures + + However when signing an existing unsigned signature form that + doesn't happen + + This breaks working on the file in Acrobat reader, e.g. it doesn't + show the signatures panel and can't save amendments to the file + + Fixes https://bugs.kde.org/show_bug.cgi?id=461371 + + poppler/Form.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit 39d0009741465344f8a30a3129081cd18bb378bb +Author: Sune Vuorela +Date: Fri Apr 14 17:12:24 2023 +0200 + + API fixup: Use optional instead of a 'None' backend + + Let's fix the cryptosign backend code before release. + + poppler/CryptoSignBackend.cc | 13 +++++++------ + poppler/CryptoSignBackend.h | 3 +-- + 2 files changed, 8 insertions(+), 8 deletions(-) + +commit 2cf3cf58ed9f70b99e6ee93c57bb434a52a0e857 +Author: Albert Astals Cid +Date: Thu Apr 27 11:50:45 2023 +0200 + + Check overflow in nvals correctly + + poppler/Stream.cc | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +commit abc60c57be8f0dfb5b3b7b47ed9a086df9010503 +Author: Albert Astals Cid +Date: Sat Apr 22 16:35:09 2023 +0200 + + CI: Update Fedora to 38 + + .gitlab-ci.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit cc5ac1665aa3056d1f90a12e12d24a02536647e0 +Author: Sune Vuorela +Date: Mon Mar 27 10:40:19 2023 +0200 + + Adapt signature code to the backend interface + + Also set the signature info a bit later; + gpgme only gives us the info after the validateSignature call + + CMakeLists.txt | 25 +++++++++++++ + config.h.cmake | 6 ++++ + poppler/Form.cc | 87 + +++++++++++++++++++++++---------------------- + poppler/Form.h | 8 +++-- + poppler/SignatureHandler.cc | 56 +++++++++++++++-------------- + poppler/SignatureHandler.h | 45 ++++++++++++----------- + qt5/src/poppler-form.cc | 30 ++++++++-------- + qt6/src/poppler-form.cc | 30 ++++++++-------- + utils/pdfsig.cc | 3 +- + 9 files changed, 168 insertions(+), 122 deletions(-) + +commit cb96047d029e2145191d79b7ece17b4a215794cd +Author: Sune Vuorela +Date: Thu Mar 23 16:40:59 2023 +0100 + + Backending infrastructure + + poppler/CryptoSignBackend.cc | 97 + +++++++++++++++++++++++++++++++++++++++++ + poppler/CryptoSignBackend.h | 101 + +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 198 insertions(+) + +commit 1be35ee8b2d6418dfafc6a8a9bbf6d70eafb2d2e +Author: Albert Astals Cid +Date: Wed Apr 5 00:19:04 2023 +0200 + + Fix stack overflow in PostScriptFunction::parseCode + + By using a pointer instead of a variable things are on the heap. + + Fixes issue #1381 + + poppler/Function.cc | 40 +++++++++++++++++++++------------------- + poppler/Function.h | 2 +- + 2 files changed, 22 insertions(+), 20 deletions(-) + +commit 8354c73ea0516a9d4a4bdf68da5c5392498b286d +Author: Oliver Sander +Date: Sun Apr 2 14:38:53 2023 +0200 + + Make filterPSName return std::string + + Rather than a pointer to GooString. This saves one memory allocation. + + poppler/PSOutputDev.cc | 41 +++++++++++++++++------------------------ + poppler/PSOutputDev.h | 2 +- + 2 files changed, 18 insertions(+), 25 deletions(-) + +commit 013ab299d7041691760fbf8626cb4125e6a5a459 +Author: Albert Astals Cid +Date: Mon Apr 3 17:10:13 2023 +0200 + + Update (C) + + poppler/GfxFont.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 341687022c6f34d6979bfebe34b8655679198d68 +Author: Albert Astals Cid +Date: Sun Apr 2 12:19:44 2023 +0200 + + GfxCIDFont::getCodeToGIDMap: Fix uninitialized memory reads + + Only initialize tumap when we're actually going to fill it + + poppler/GfxFont.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 4c6da80ab5a54df72c0243d32a3976b683441d5e +Author: Albert Astals Cid +Date: Sun Apr 2 11:26:06 2023 +0200 + + poppler 23.04.0 + + CMakeLists.txt | 4 ++-- + NEWS | 12 ++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 17 insertions(+), 5 deletions(-) + +commit 23994cf44ac8a11b0dfbf159df669bbe1ac78dc3 +Author: Oliver Sander +Date: Wed Mar 29 12:31:18 2023 +0200 + + Use std::vector for the fontIDs array + + This makes the code safer and easier to read. + + poppler/PSOutputDev.cc | 24 ++++++------------------ + poppler/PSOutputDev.h | 6 ++---- + 2 files changed, 8 insertions(+), 22 deletions(-) + +commit 09aed1a65e703ec419980506d12578ca4149f58d +Author: Oliver Sander +Date: Wed Mar 29 08:36:49 2023 +0200 + + Use std::string for psFileName, instead of GooString + + This simplifies the memory and error handling a little bit. + + utils/pdftops.cc | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +commit 770b0e130183104e30eb9138a68be187a689737b +Author: Albert Astals Cid +Date: Tue Mar 28 12:34:10 2023 +0200 + + CI: fix mingw + + .gitlab-ci.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 098d2d497b3d8cbc4fdf9eb68385bfbe2ee3e711 +Author: Sune Vuorela +Date: Mon Mar 27 17:52:28 2023 +0200 + + Fix crash in bad signature verification + + If the signature existing but too wrong for us to understand, we might + not get the hashContext object created, so guard against that. + + This is fallout from 08c0766e9d. + + Note the SignHandler does not need the same guards; the hashContext is + always present. + + poppler/SignatureHandler.cc | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +commit 08c0766e9d7ab08cdc7ef380ce9c190ae87d789b +Author: Sune Vuorela +Date: Fri Mar 3 08:22:13 2023 +0100 + + Split signature handler for signing and verification + + Only a little bit of the class state was shared between the signing + and verification functions, so split it in the shared hashing bits + and a class for signing and one for verification + + Also clean up a little bit of the memory handling + + poppler/Form.cc | 8 +-- + poppler/Form.h | 4 +- + poppler/SignatureHandler.cc | 125 + +++++++++++++++++++------------------------- + poppler/SignatureHandler.h | 64 +++++++++++++++-------- + qt5/src/poppler-form.cc | 2 +- + qt6/src/poppler-form.cc | 2 +- + 6 files changed, 105 insertions(+), 100 deletions(-) + +commit e7fa64daa62bd09839d9d52413600889d76ae936 +Author: Albert Astals Cid +Date: Mon Mar 27 11:42:08 2023 +0200 + + clang-format-15 reformat to ignore-revs file + + .git-blame-ignore-revs | 2 ++ + 1 file changed, 2 insertions(+) + +commit b5aa09403e0f106f51292683c1841908167337ea +Author: Albert Astals Cid +Date: Wed Oct 12 11:59:27 2022 +0200 + + clang 15 + + .clang-tidy | 2 +- + .gitlab-ci.yml | 20 ++-- + README.contributors | 2 +- + glib/poppler-document.cc | 2 +- + goo/gfile.h | 10 +- + poppler/CairoOutputDev.h | 104 ++++--------------- + poppler/Gfx.h | 10 +- + poppler/GfxState.h | 65 +++--------- + poppler/GlobalParams.cc | 6 +- + poppler/OutputDev.h | 107 ++++--------------- + poppler/PDFDoc.h | 260 + ++++++++++------------------------------------ + poppler/PSOutputDev.h | 155 ++++++--------------------- + poppler/SplashOutputDev.h | 15 +-- + poppler/TextOutputDev.h | 125 +++++----------------- + qt5/src/poppler-private.h | 5 +- + qt6/src/poppler-private.h | 5 +- + splash/Splash.h | 15 +-- + test/cairo-thread-test.cc | 2 +- + test/perf-test.cc | 2 +- + utils/InMemoryFile.h | 5 +- + 20 files changed, 195 insertions(+), 722 deletions(-) + +commit af911848aa1e3c4bdb90558dc9c8aef2d6498cdf +Author: Albert Astals Cid +Date: Sun Mar 26 23:48:13 2023 +0200 + + Update (C) + + poppler/Page.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit ac5cc0244f1131687f7699115e3c1be415c55409 +Author: Albert Astals Cid +Date: Sun Mar 26 23:37:42 2023 +0200 + + Fix leak when calling addAnnot on malformed files + + oss-fuzz/57464 + + poppler/Page.cc | 13 ++++++++++--- + poppler/Page.h | 4 ++-- + 2 files changed, 12 insertions(+), 5 deletions(-) + +commit d5d23b3bcc4cbfbe58bdfa3e2c82555aa72c4117 +Author: Albert Astals Cid +Date: Fri Mar 24 23:52:02 2023 +0100 + + Update (C) + + poppler/Page.cc | 2 +- + poppler/XRef.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +commit d07818d63677ccd2ea68b580ee45085597681a36 +Author: Albert Astals Cid +Date: Thu Mar 23 19:22:21 2023 +0100 + + Page::removeAnnot: Reorder code to fix memory leak + + We were removing the annot object from the xref, then calling + removeReferencedObjects + that in some cases calls setModifiedObject on the xref with that same + annot object, which is wrong, we can't modify an object that isn't + there + anymore + + poppler/Page.cc | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +commit 96a953dbde5339798ca583a5ced2e6f0384351e7 +Author: Albert Astals Cid +Date: Thu Mar 23 19:21:16 2023 +0100 + + XRef: Add a warning if calling setModifiedObject on empty ref + + poppler/XRef.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit ea5e64f139094ec93b2261d51a61c8a8b9b57e44 +Author: Albert Astals Cid +Date: Fri Mar 17 00:14:56 2023 +0100 + + PDFDoc::sign: Fix memory issue when signing fails + + Issue #1372 + + poppler/PDFDoc.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 3d38bd6abacc607e492361f8febf381065913d36 +Author: Sune Vuorela +Date: Thu Mar 23 09:38:47 2023 +0000 + + Fix use of dangling memory + + g_strdup is a macro and thus the intermediate getSignerName() value + would be destructed after the first line of code in the macro + + glib/poppler-form-field.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit f8d8753fecdb8b4e572d3aec6c654c5efb498b44 +Author: Sune Vuorela +Date: Thu Mar 16 09:45:02 2023 +0100 + + Split HashContext to separate class + + Then we only need one place to do the endHash and memory handling. + + poppler/SignatureHandler.cc | 94 + ++++++++++++++++++++------------------------- + poppler/SignatureHandler.h | 27 +++++++++---- + 2 files changed, 60 insertions(+), 61 deletions(-) + +commit 3a3965a5cc7f20fd0d6cbfe92a45b3999456d7a0 +Author: Sune Vuorela +Date: Tue Mar 14 09:47:51 2023 +0100 + + Remove need for dual signing of data + + Instead of creating a placeholder signature of dummy data, just use + dummy data as a placeholder and then pad the real signature with + zeroes + to fit in the same size as the dummy data. + + This is also kind of what the pdf specification suggests. + + It also ensures that for certain smart card setups requiring + a distinct + pin per signature will only be asked once. + + poppler/Form.cc | 30 +++++++++++++++--------------- + poppler/Form.h | 2 +- + poppler/SignatureHandler.cc | 7 +------ + poppler/SignatureHandler.h | 6 +++++- + 4 files changed, 22 insertions(+), 23 deletions(-) + +commit 22b794c0535a0f3331428b570d348ee51ca980b8 +Author: Sune Vuorela +Date: Wed Mar 22 17:02:53 2023 +0100 + + Minor hardening + + I'm not sure how, but I succeded creating a null certificate info ptr, + so better guard that for being safe. + + If getting the start/end fails, the objStart/objEnd might not be + initialized, so guard against that. The document would be quite + malformed to get here in the first place. + + poppler/Form.cc | 5 +++++ + 1 file changed, 5 insertions(+) + +commit 7608a427558e54ec401e6f5600ee0e9f4cf6f392 +Author: Sune Vuorela +Date: Wed Mar 22 19:10:37 2023 +0100 + + CI: Use FDO_CI_CONCURRENT + + the freedesktop.org ci system suggests to not use more parallel jobs + than whatever is put in that variable to avoid killing the systems. + + .gitlab-ci.yml | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +commit fbc26317d9bd869452d4d3da0ba05077ae619748 +Author: Albert Astals Cid +Date: Tue Mar 21 23:12:03 2023 +0100 + + pdfsig: Support --help + + utils/pdfsig.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 9ff937311f0764e5f339fbbe15899a9dd6e006f0 +Author: Albert Astals Cid +Date: Mon Mar 20 14:37:32 2023 +0100 + + Update (C) + + poppler/Form.h | 1 + + poppler/PDFDoc.cc | 1 + + poppler/PDFDoc.h | 1 + + 3 files changed, 3 insertions(+) + +commit e5a3052390b5912b19c749e91babfe3d83004184 +Author: Sune Vuorela +Date: Thu Mar 16 10:20:22 2023 +0100 + + Use vector, not malloc/free for signature data + + poppler/Form.cc | 6 +++--- + poppler/SignatureHandler.cc | 7 +++---- + poppler/SignatureHandler.h | 3 ++- + 3 files changed, 8 insertions(+), 8 deletions(-) + +commit 5ccac116d19c29bc543849a1540921b55c313aa8 +Author: Sune Vuorela +Date: Mon Mar 20 12:07:24 2023 +0100 + + pdfsig -dump - allow holes in signature list + + It is perfectly possible to have e.g. 4 signature slots and only + currently a signature in slot first and last slot. pdfsig should + be able + to dump all of them, but it considers a missing signature a stop + condition. It can just continue and get all of actual ones out. + + utils/pdfsig.cc | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +commit 2b53d5a8ccb7350c00ef135ab40ca3562099265c +Author: Sune Vuorela +Date: Mon Mar 20 11:48:42 2023 +0100 + + Fixup for 8787103a43 + + Some codepaths was overlooked; might lead to crashes in pdfsig. + + Also clean up the Qt checkpassword functions; they aren't crashers + though. + + qt5/src/poppler-form.cc | 4 ++-- + qt6/src/poppler-form.cc | 2 +- + utils/pdfsig.cc | 3 +-- + 3 files changed, 4 insertions(+), 5 deletions(-) + +commit 8787103a43732440e1994c6c72d99a4717af8b95 +Author: Sune Vuorela +Date: Fri Mar 17 14:08:42 2023 +0100 + + Switch some const char* to std string + + Also move the weirdness (empty password is nullptr) close to usage, + rather than have it as far away as possible. + + poppler/Form.cc | 14 +++++++------- + poppler/Form.h | 8 ++++---- + poppler/PDFDoc.cc | 6 +++--- + poppler/PDFDoc.h | 6 +++--- + poppler/SignatureHandler.cc | 8 ++++---- + poppler/SignatureHandler.h | 4 ++-- + qt5/src/poppler-form.cc | 5 ++--- + qt6/src/poppler-form.cc | 7 +++---- + utils/pdfsig.cc | 3 +-- + 9 files changed, 29 insertions(+), 32 deletions(-) + +commit b39c8cd2bc7757b3c1c4756de345702f852a4706 +Author: Sune Vuorela +Date: Fri Mar 17 14:24:51 2023 +0100 + + Add const to a few signature methods + + poppler/SignatureHandler.cc | 8 ++++---- + poppler/SignatureHandler.h | 8 ++++---- + 2 files changed, 8 insertions(+), 8 deletions(-) + +commit ede53de1654afe86e25fa22c7e9d50003a217d5a +Author: Sune Vuorela +Date: Mon Mar 13 14:48:21 2023 +0100 + + Valgrind cleanup of signing + + signing_cert is owned by us while verifying, but the data we were + writing to it while signing was not (directly) owned by us, so don't + free it. Or well. Don't write it to a class member. + + poppler/SignatureHandler.cc | 30 +++++++++++++++++++----------- + poppler/SignatureHandler.h | 2 +- + 2 files changed, 20 insertions(+), 12 deletions(-) + +commit fc802d21df3c70f721c0353ab8862a4632c8018e +Author: Sune Vuorela +Date: Thu Mar 16 09:54:54 2023 +0100 + + Make standalone methods standalone + + In previous commits, these functions has been made fully standalone, + so + now make it obvious. + + poppler/SignatureHandler.cc | 10 +++++++--- + poppler/SignatureHandler.h | 3 --- + 2 files changed, 7 insertions(+), 6 deletions(-) + +commit e4f80d6461cc26e2dd1494ab2bf548e3382a5153 +Author: Sune Vuorela +Date: Wed Mar 15 12:22:00 2023 +0100 + + Simple signature tests + + Document some of the fields we can extract. + + qt5/tests/CMakeLists.txt | 1 + + qt5/tests/check_signature_basics.cpp | 133 + +++++++++++++++++++++++++++++++++++ + qt6/tests/CMakeLists.txt | 1 + + qt6/tests/check_signature_basics.cpp | 133 + +++++++++++++++++++++++++++++++++++ + 4 files changed, 268 insertions(+) + +commit e18bdd936d9da551506715f8f7e36c13599c9728 +Author: Albert Astals Cid +Date: Thu Mar 16 00:53:10 2023 +0100 + + SignatureHandler::validateSignature: Use the actual hash length + + It's going to be the same size, but this is more proper + + poppler/SignatureHandler.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +commit 829d03cccd77312886e0bc215366cfe600e65044 +Author: Sune Vuorela +Date: Mon Mar 13 14:05:59 2023 +0100 + + Switch second digest buffer to not leak memory + + poppler/SignatureHandler.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit a691d8d5e6ce169325542b7912d61a215f412f28 +Author: Sune Vuorela +Date: Mon Mar 13 13:51:55 2023 +0100 + + Don't manually handle the digest buffer memory + + Also fix length and content comparison to happen in the right order + + poppler/SignatureHandler.cc | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +commit cfea6a76be067a37710f181480e94cee9b63f578 +Author: Albert Astals Cid +Date: Wed Mar 15 00:38:22 2023 +0100 + + Update (C) + + poppler/CairoFontEngine.cc | 2 ++ + 1 file changed, 2 insertions(+) + +commit fb5da5c231102f1577fb257581f9a8588e0dda5e +Author: Pablo Correa Gómez +Date: Mon Mar 13 22:20:11 2023 +0100 + + CairoFontEngine: remove unused variables from type3_font_info_t + + poppler/CairoFontEngine.cc | 9 ++------- + 1 file changed, 2 insertions(+), 7 deletions(-) + +commit 479a069a859f44a6cbcd3add6d00afea9bc26b95 +Author: Frederic Germain +Date: Tue Mar 7 16:37:10 2023 +0100 + + CairoFontEngine: improve type3 font rendering + + Cache Gfx context + Some PDFs takes time to load, this would improve from a + few seconds loading time to instant display + + Coauthored-by: Pablo Correa Gomez + + poppler/CairoFontEngine.cc | 42 + +++++++++++++++++++++++++++--------------- + 1 file changed, 27 insertions(+), 15 deletions(-) + +commit fb49889fea6e6003d8b8e2d65de0ce58d6229d54 +Author: Sune Vuorela +Date: Mon Mar 13 13:56:59 2023 +0100 + + Simplify temp_certs memory handling + + poppler/SignatureHandler.cc | 20 ++++++++++++++------ + poppler/SignatureHandler.h | 1 - + 2 files changed, 14 insertions(+), 7 deletions(-) + +commit fcb7b90ddbd6135e3fbf1032de07bc5b0e351df2 +Author: Sune Vuorela +Date: Mon Mar 13 14:28:08 2023 +0100 + + nss created message was leaked on most errors + + Use a unique_ptr to ensure it is being destructed properly + + poppler/SignatureHandler.cc | 19 +++++++++++-------- + 1 file changed, 11 insertions(+), 8 deletions(-) + +commit 01ff2af0ee61c45bf4728a1d6ff4327aa2889d21 +Author: Albert Astals Cid +Date: Mon Mar 13 15:49:09 2023 +0100 + + Update (C) + + utils/pdftocairo.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 2cd82736e2db45e9a06baba1478439637172a76e +Author: Albert Astals Cid +Date: Mon Mar 13 15:45:41 2023 +0100 + + Update (C) + + poppler/CertificateInfo.cc | 1 + + poppler/CertificateInfo.h | 1 + + 2 files changed, 2 insertions(+) + +commit bd533d75bb171dada6fa9e9fdc5bdc3e42824b97 +Author: Sune Vuorela +Date: Mon Mar 13 14:21:23 2023 +0100 + + Put the arenapool in a unique_ptr + + Also allocate it a bit earlier to use it for a few other entries that + might otherwise else be leaked in certain error conditions + + poppler/SignatureHandler.cc | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +commit 7b50d9f0374aa5d0ea653a0d024b315e16625839 +Author: Sune Vuorela +Date: Mon Mar 13 14:02:27 2023 +0100 + + Put HASHContext in a unique_ptr rather than manually manage it + with freeing + + poppler/SignatureHandler.cc | 19 ++++++------------- + poppler/SignatureHandler.h | 6 +++++- + 2 files changed, 11 insertions(+), 14 deletions(-) + +commit c630bb95af5790d8d438d7e3b1dae8bf9e4a1541 +Author: Anton Thomasson +Date: Sun Mar 12 10:10:03 2023 +0100 + + pdftocairo: Don't do things based on first page + + Remember document initialization status instead. + This prevents a segfault where even/odd rendering forgets to run + beginDocument as the "first" page is not to be rendered. + + utils/pdftocairo.cc | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +commit 90da1d7b9ee200a0006bd9a103a3da10e2022e3b +Author: Albert Astals Cid +Date: Fri Mar 10 23:27:51 2023 +0100 + + Fix memory leak in GlobalParams::findSystemFontFileForFamilyAndStyle + + poppler/GlobalParams.cc | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +commit b46e395c39700325cd6d717f0cf459274a44a1a9 +Author: Sune Vuorela +Date: Fri Mar 10 14:10:32 2023 +0100 + + Simplify certificate and signature info + + More stuff can be defaulted. + + poppler/CertificateInfo.cc | 25 ------------------------- + poppler/CertificateInfo.h | 18 +++++++++--------- + poppler/SignatureInfo.cc | 22 +--------------------- + poppler/SignatureInfo.h | 13 ++++++------- + 4 files changed, 16 insertions(+), 62 deletions(-) + +commit 96067bdbc2307decacc8d526795fd2dacf617f5d +Author: Albert Astals Cid +Date: Wed Mar 8 23:07:14 2023 +0100 + + Update (C) + + glib/poppler-form-field.cc | 1 + + 1 file changed, 1 insertion(+) + +commit c335934c1c30ab3ec1dd963f4646ae88d5790172 +Author: Sune Vuorela +Date: Tue Mar 7 15:30:55 2023 +0100 + + Use std::string for strings + + not manual strdup/free + + glib/poppler-form-field.cc | 2 +- + poppler/SignatureInfo.cc | 28 +++++++++------------------- + poppler/SignatureInfo.h | 12 ++++++------ + qt5/src/poppler-form.cc | 4 ++-- + qt6/src/poppler-form.cc | 4 ++-- + utils/pdfsig.cc | 4 ++-- + 6 files changed, 22 insertions(+), 32 deletions(-) + +commit 89bf50a84ebbca98916384d7dd3614225f02d2cb +Author: Albert Astals Cid +Date: Wed Mar 8 01:15:33 2023 +0100 + + Update (C) + + poppler/Form.cc | 3 ++- + poppler/Form.h | 2 +- + poppler/PDFDoc.cc | 2 +- + poppler/SignatureHandler.cc | 2 ++ + poppler/SignatureHandler.h | 1 + + poppler/SignatureInfo.cc | 1 + + poppler/SignatureInfo.h | 1 + + qt5/src/poppler-form.cc | 3 ++- + qt6/src/poppler-form.cc | 3 ++- + utils/pdfsig.cc | 3 ++- + 10 files changed, 15 insertions(+), 6 deletions(-) + +commit d367fe8d40b3d3d19285cd36c1a76447feea22db +Author: Albert Astals Cid +Date: Sat Mar 4 01:19:51 2023 +0100 + + Remove unused digestName parameter + + poppler/Form.cc | 10 +++++----- + poppler/Form.h | 8 ++++---- + poppler/PDFDoc.cc | 2 +- + qt5/src/poppler-form.cc | 2 +- + qt6/src/poppler-form.cc | 2 +- + utils/pdfsig.cc | 8 +++++++- + 6 files changed, 19 insertions(+), 13 deletions(-) + +commit 6051948976b63ffa02a93afc9aaea18d2733a4f9 +Author: Sune Vuorela +Date: Thu Mar 2 14:41:48 2023 +0100 + + Remove unused static method + + poppler/SignatureHandler.cc | 15 --------------- + poppler/SignatureHandler.h | 2 -- + 2 files changed, 17 deletions(-) + +commit 7af9565c492205cdad27c08478168d9e53349f83 +Author: Sune Vuorela +Date: Thu Mar 2 14:17:15 2023 +0100 + + Remove unused constructor + + poppler/SignatureHandler.cc | 6 ------ + poppler/SignatureHandler.h | 1 - + 2 files changed, 7 deletions(-) + +commit f688173900f3c29e802a3c140bdaa9d48d6c7118 +Author: Sune Vuorela +Date: Thu Mar 2 12:48:19 2023 +0100 + + Remove unused ASN bits + + poppler/SignatureHandler.cc | 64 + --------------------------------------------- + 1 file changed, 64 deletions(-) + +commit 64c7f8b3b67902274684f6541c0a931001a6fa20 +Author: Sune Vuorela +Date: Thu Mar 2 12:23:10 2023 +0100 + + Remove unused code + + poppler/SignatureHandler.h | 13 ------------- + 1 file changed, 13 deletions(-) + +commit c109770d59cb81d3dc8f3ab8a3ab7795e3583478 +Author: Ingo Klöcker +Date: Thu Mar 2 13:18:45 2023 +0000 + + add const + + poppler/SignatureHandler.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 56fc0b14214feb50c377c725bc55d53738881532 +Author: Sune Vuorela +Date: Thu Mar 2 12:02:44 2023 +0100 + + Keep nss types in signature class + + If one wants the ability to use a different library for signing and + verification than nss, it is a good idea to not directly expose + the NSS + types to the rest of poppler. + + poppler/Form.cc | 2 +- + poppler/HashAlgorithm.h | 25 +++++++++++ + poppler/SignatureHandler.cc | 106 + ++++++++++++++++++++++++++++++++++++-------- + poppler/SignatureHandler.h | 11 ++--- + poppler/SignatureInfo.cc | 14 ++---- + poppler/SignatureInfo.h | 7 +-- + qt5/src/poppler-form.cc | 20 +++++---- + qt6/src/poppler-form.cc | 20 +++++---- + utils/pdfsig.cc | 14 +++--- + 9 files changed, 157 insertions(+), 62 deletions(-) + +commit 051c2601ecc35864cad6172db8387b64951cf859 +Author: Albert Astals Cid +Date: Wed Mar 1 22:15:25 2023 +0100 + + poppler 23.03.0 + + CMakeLists.txt | 2 +- + NEWS | 8 ++++---- + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 8 insertions(+), 8 deletions(-) + +commit 083662257ff626d6d3454a37b70928a62990c7ad +Author: Albert Astals Cid +Date: Sat Feb 4 11:32:08 2023 +0100 + + Update (C) + + goo/PNGWriter.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 329ec39cb876f0d4137eff4fbb5e4ce37601b71a +Author: Albert Astals Cid +Date: Thu Feb 2 23:43:52 2023 +0100 + + PngWriter: Fix uninitialized memory use + + When just created and deleted without having called init() + + goo/PNGWriter.cc | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +commit 7673e1fd712416a5d65406d7447a1c3d5ada057b +Author: Albert Astals Cid +Date: Wed Feb 1 17:42:42 2023 +0100 + + poppler 23.02.0 + + CMakeLists.txt | 2 +- + NEWS | 16 +++++++++++++++- + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 19 insertions(+), 5 deletions(-) + +commit 06a0e17bd9387da0a9737f6ce945482930d98341 +Author: Albert Astals Cid +Date: Thu Jan 26 19:07:58 2023 +0100 + + Update (C) + + poppler/CairoOutputDev.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 1327646900994c033155100adf7bbaad7654ff50 +Author: Artemy Gordon +Date: Thu Jan 26 18:01:09 2023 +0000 + + Add handling matte entry in cairo backend + + poppler/CairoOutputDev.cc | 47 + ++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 5 deletions(-) + +commit 5f76a61237db0fe26bb2ac41ac41d20670d985a3 +Author: Albert Astals Cid +Date: Thu Jan 26 00:27:47 2023 +0100 + + Update (C) + + poppler/CairoFontEngine.cc | 2 +- + poppler/CairoOutputDev.cc | 2 +- + poppler/CairoOutputDev.h | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +commit 53115a05b7b4480ef06165199c8f1cb3982ab13d +Author: Adrian Johnson +Date: Tue Jan 3 10:13:52 2023 +1030 + + Fix cairo rendering of color type 3 fonts + + The bug fix in 448f03cf needs to be disabled for color fonts. + + poppler/CairoFontEngine.cc | 2 +- + poppler/CairoOutputDev.cc | 6 +++--- + poppler/CairoOutputDev.h | 12 +++++++++--- + 3 files changed, 13 insertions(+), 7 deletions(-) + +commit da39bd930fee9369071a97b93ffe3ea0987acedc +Author: Tobias Deiminger +Date: Tue Jan 3 00:25:57 2023 +0100 + + Point out pdfsig supports PKCS#11 URIs as nickname + + NSS "just works" with PKCS#11 URIs since 3.39. See + https://bugzilla.mozilla.org/show_bug.cgi?id=1475274 + for details. + + IMO we should expose that as feature. It's a standardized NSS-agnostic + way to identify certificate objects, and allows to disambiguate + certificates in any case. + + utils/pdfsig.1 | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +commit 63bfacc89576345722cf3cefb962861aa7d159b8 +Author: Tobias Deiminger +Date: Wed Jan 4 21:26:12 2023 +0100 + + Fix "NSS could not shutdown" + + SignatureHandler never freed it's signing certificate. This left + an internal NSS reference that prevented cleanup of the NSS cache, + causing "NSS could not shutdown". + + NSS maintains an internal in-memory cache, where content is reference + counted. PK11_MakeCertFromHandle increases the issuerAndSn count. + CERT_DestroyCertificate decreases it. NSS tries to destruct the + cache in + NSS_Shutdown, which we've scheduled with atexit. There it detects a + remaining reference. + + Fixes #1326. + + poppler/SignatureHandler.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 924ef4264cdb0eca97185d7fc1791bcf32279933 +Author: Albert Astals Cid +Date: Mon Jan 9 22:52:01 2023 +0100 + + Update (C) + + poppler/SignatureHandler.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 4f8c8297d7190480500cdafe52c8777202229b30 +Author: Tobias Deiminger +Date: Mon Jan 9 21:00:06 2023 +0100 + + Fix segfault on wrong nssdir + + If SignatureHandler was used with a custom DB directory, but that + directory didn't exist or contained no valid DB, NSS crashed on + subsequent calls. + + We can prevent crashes by calling NSS_NoDB_Init (as it's already + done in + the default-DB case). + + Fixes #1331. + + poppler/SignatureHandler.cc | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +commit 4259ff0c2067d302f97d87221a442eec8e88d45c +Author: Albert Astals Cid +Date: Sun Jan 1 13:24:17 2023 +0100 + + poppler 23.01.0 + + CMakeLists.txt | 4 ++-- + NEWS | 18 ++++++++++++++++++ + cpp/Doxyfile | 2 +- + qt5/src/Doxyfile | 2 +- + qt6/src/Doxyfile | 2 +- + 5 files changed, 23 insertions(+), 5 deletions(-) + +commit 2e3bf1476558d11a72b9f3e574eb28bb6ab2ad66 +Author: Albert Astals Cid +Date: Sun Jan 1 13:11:30 2023 +0100 + + Welcome 2023 + + poppler/poppler-config.h.cmake | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 7f0276bbc5f7f7b4fefa956d8893ee23c13226e2 +Author: Albert Astals Cid +Date: Sat Dec 31 01:01:08 2022 +0100 + + CI: Cheat a bit in the build_clang14_libcpp build + + .gitlab-ci.yml | 3 ++- + qt6/tests/CMakeLists.txt | 4 +++- + 2 files changed, 5 insertions(+), 2 deletions(-) + +commit 43e2da4ade60f7e6c62b4d70f15d603327ca7b2b +Author: Albert Astals Cid +Date: Fri Dec 30 23:49:15 2022 +0100 + + Update (C) + + poppler/CairoOutputDev.cc | 1 + + 1 file changed, 1 insertion(+) + +commit 7d14f465a6441f3df4389df375b154687e1fdb6b +Author: Zachary Travis +Date: Fri Aug 13 20:33:35 2021 -0700 + + Ignore text rendering mode for type3 fonts + + poppler/CairoOutputDev.cc | 6 ++++++ + 1 file changed, 6 insertions(+) + +commit f9d958ddee1e9a77d41c80793fd20ce5299f08ef +Author: Albert Astals Cid +Date: Tue Dec 27 09:38:55 2022 +0100 + + Gfx::opBeginMarkedContent: Support Span with Name + + Issue #1327 + + poppler/Gfx.cc | 23 ++++++++++++++++------- + 1 file changed, 16 insertions(+), 7 deletions(-) + +commit 7dda4190b273edb2c5d74b7f5bc7cc77c49c9dbb +Author: Albert Astals Cid +Date: Wed Dec 14 23:12:56 2022 +0100 + + Update (C) + + poppler/SplashOutputDev.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit eac1066f60df2442c5508fb1601fa6a331cac7a0 +Author: Thomas Freitag +Date: Wed Dec 14 22:05:33 2022 +0000 + + Splash: avoid problems because of implicit rounding in non-separable + blend modes + + Splash uses unsigned char to store color pixel. But in non-separable + blend modes int and in some calculations float values are used. To + avoid rounding problems because of implicit rounding calculations + are now done with int values and when storing int values in unsigned + char variables clamp is used! + + poppler/SplashOutputDev.cc | 51 + ++++++++++++++++------------------------------ + 1 file changed, 17 insertions(+), 34 deletions(-) + +commit 065dca3816db3979dfacdc2f8592abed2ff6859a +Author: Albert Astals Cid +Date: Tue Dec 13 22:31:20 2022 +0100 + + FoFiType1::parse: Be more flexible parsing the encoding content + + Don't force it to be on a single line. + + Fixes #1324 + + fofi/FoFiType1.cc | 203 + ++++++++++++++++++++++++++++++------------------------ + 1 file changed, 113 insertions(+), 90 deletions(-) + +commit a9e1987591ed3896e18a81d0b66f65adf66f41cb +Author: Albert Astals Cid +Date: Tue Dec 13 00:48:59 2022 +0100 + + Remove unused FoFiType1::load function + + fofi/FoFiType1.cc | 11 ----------- + fofi/FoFiType1.h | 5 +---- + 2 files changed, 1 insertion(+), 15 deletions(-) + +commit a563801e1a6be5e70645472d82f4ba8534b454d1 +Author: Albert Astals Cid +Date: Mon Dec 12 23:40:24 2022 +0100 + + PDFDoc::sign: Find Arial to sign if Helvetica isn't found + + At least on my Windows install Helvetica isn't found and signing thus + fails + + poppler/PDFDoc.cc | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +commit 09fdfecea3a13d30b5c52e1258d17549739d9ea8 +Author: Albert Astals Cid +Date: Mon Dec 12 23:28:55 2022 +0100 + + PDFDoc::sign: Fix crash if font can't be found + + poppler/PDFDoc.cc | 4 ++++ + 1 file changed, 4 insertions(+) + +commit 6a19713ca8473c14905bd25d719757df5987d93b +Author: Albert Astals Cid +Date: Sun Dec 4 11:43:16 2022 +0100 + + cmake: A more bit of include tweaking + + test/CMakeLists.txt | 5 ----- + utils/CMakeLists.txt | 2 -- + 2 files changed, 7 deletions(-) + +commit 49a883dfac1a92fd959a1421627279aec26dcbb9 +Author: Albert Astals Cid +Date: Sun Dec 4 03:57:01 2022 +0100 + + cmake: Make sure cairo includes are complete + + cmake/modules/FindCairo.cmake | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 9bd49f486ed8442943a08cfee42136a5575c6a1b +Author: Albert Astals Cid +Date: Sun Dec 4 03:22:52 2022 +0100 + + cmake: Add CAIRO_INCLUDE_DIRS to the appropriate targets + + glib/CMakeLists.txt | 1 + + test/CMakeLists.txt | 3 +++ + utils/CMakeLists.txt | 1 + + 3 files changed, 5 insertions(+) + +commit 821784a6f08adfbb5211d665389acc275e035e84 +Author: Albert Astals Cid +Date: Sun Dec 4 03:02:30 2022 +0100 + + cmake: Add missing LCMS2_INCLUDE_DIR + + qt5/src/CMakeLists.txt | 1 + + qt6/src/CMakeLists.txt | 1 + + utils/CMakeLists.txt | 3 +++ + 3 files changed, 5 insertions(+) + +commit 77d1660d6b6777a434a4c09f6c781999ab7ce0a8 +Author: Albert Astals Cid +Date: Sun Dec 4 02:42:15 2022 +0100 + + cmake: include dirs via target_include_directories + + instead of include_directories + + CMakeLists.txt | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +commit b1fe3a03c0bfdc4df6d8dd5d128b928d3cb2ea89 +Author: Albert Astals Cid +Date: Sun Dec 4 02:20:27 2022 +0100 + + cmake: nss: Fix typo and remove "deprecated" variables + + cmake/modules/FindNSS3.cmake | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +commit 46b185c25472cb1cb90b9f83229a920fd638fd02 +Author: Albert Astals Cid +Date: Sun Dec 4 02:17:56 2022 +0100 + + Increase our minimum supported NSS to that of Ubuntu 20.04 too + + cmake/modules/FindNSS3.cmake | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +commit 2944c16a1ac7da0aaf9cb351ff664585968d8356 +Author: Albert Astals Cid +Date: Fri Dec 2 22:55:51 2022 +0100 + + Fix crash on broken documents + + Issue #1320 + + splash/Splash.cc | 3 +++ + 1 file changed, 3 insertions(+) + +commit 54c93aade58b60699c0bf2c7962c274b6e6f7772 +Author: Albert Astals Cid +Date: Thu Dec 1 22:56:16 2022 +0100 + + Qt6: Less deprecated functions + + qt6/src/poppler-annotation.cc | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +commit 40c737a450153bdde180e0ee6e069a08c8e04dc4 +Author: Albert Astals Cid +Date: Thu Dec 1 22:08:40 2022 +0100 + + CI: Install Qt 6.2 in build_ubuntu_20_04 + + now that the other systems ship Qt 6.3 or newer we need at least + one CI + with our minimum required Qt + + .gitlab-ci.yml | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +commit df3746fa4c9413422bb70dbbb61108922a7fddc9 +Author: Albert Astals Cid +Date: Fri Jul 29 00:58:42 2022 +0200 + + cmake: Check we have a new enough libjpeg + + CMakeLists.txt | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +commit d5ea5a24124badf2b32a7d08dd2c06a4a40f93fb +Author: Albert Astals Cid +Date: Tue Mar 8 14:32:54 2022 +0100 + + Increase Minimum supported base to that provided by Ubuntu 20.04 + + .gitlab-ci.yml | 4 +-- + CMakeLists.txt | 58 + +++++++++++++++++++--------------- + cmake/modules/FindFontconfig.cmake | 52 ------------------------------- + cmake/modules/FindIconv.cmake | 64 + -------------------------------------- + cpp/CMakeLists.txt | 3 +- + poppler/GfxFont.cc | 14 ++++----- + qt5/CMakeLists.txt | 2 +- + 7 files changed, 44 insertions(+), 153 deletions(-) + commit df568263c51950ceed6f1fb42f80e99a2614c275 Author: Albert Astals Cid Date: Thu Dec 1 21:19:03 2022 +0100 diff --git a/NEWS b/NEWS index b62f346..61e3530 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,412 @@ +Release 25.03.0: + core: + * Fix opening some slightly malformed files + * Signing: Support custom PGP signing + * Signing: Do not embed fonts in the case of signatures without appearance + * Signing: Improve error handling on bad passphrase + * Signing-GPGME: more correct validation handling + * Internal code improvements + + glib: + * Fix default signature colors + * Check file attachment annotation for this class before casting + +Release 25.02.0: + core: + * cairo: fix issue for images reaching MAX_CAIRO_IMAGE_SIZE + * Improve rendering in some malformed documents + * Internal code improvements + + glib: + * add new api to allow selecting annotations to render + * fix a crash when getting a font description for a free text annotation if it was not set + +Release 25.01.0: + core: + * TextOutputDev: Better detect fakebold words + * TextOutputDev: Faster sorting algorithm + * Internal code improvements + * Fix crashes in malformed documents + + cpp: + * Remove rect parameter from image::copy, it was never implemented + + qt6: + * Fix crash in Submit Forms if document links to non existing field + + qt5: + * Fix crash in Submit Forms if document links to non existing field + + glib: + * mark transfer of poppler_page_get_selected_region + + utils: + * pdfdetach: Make -savefile work with Unicode strings. Issue #1540 + + build system: + * Don't update pdfsig.pot when POT-Creation-date is the only change + +Release 24.12.0: + core: + * cairo: Fix rendering of JPX images with transparency + * cairo: Fix JPX transparency regression + * Override encoding for Zapf Dingbats + * Improve Signature appearance generation code + * Improve Stamp appearance generation code + * Mark certificates that can do qualified signing + * Internal code improvements + + qt6: + * Mark certificates that can do qualified signing + + glib: + * Add free text annotations support + + utils: + * pdfsig: Improve man page + + build system: + * Install some more internal headers + * Fix build with in-tree build + +Release 24.11.0: + core: + * Fix rendering of some buttons + * Fix selecting fonts in TTC font collections + * CairoOutputDev: prevent infinite loop in functionShadedFill() + * Fix some unstable headers not being usable on their own + * Fix some headers not being usable on their own + + glib: + * Fix some headers not being usable on their own + * add INSTALL_GLIB_DEMO option to install poppler-glib-demo + * Fix Launch action with unicode encoded filenames + + qt6: + * Fix memory handling issue on signature creation + * Fix Launch action with unicode encoded filenames + + qt5: + * Fix Launch action with unicode encoded filenames + + build system: + * Remove duplicate definitions + * cmake: Set CMAKE_C_STANDARD to 17 + +Release 24.10.0: + core: + * Add support for SubmitForms Links + * Internal code improvements + * Fix crash in malformed documents + + qt6: + * Add support for SubmitForms Links + * Add API for creating signature field without signing immediately + + glib: + * add auto_cleanup functions so that consumers can use g_auto* + * fix closure GI annotation + + qt5: + * Add support for SubmitForms Links + + build system: + * Use the boosts own cmake file as suggested by cmake itself + +Release 24.09.0: + core: + * Catalog: map page by Ref to make findPage() faster + * Catalog: Add support for OpenAction + * CairoFontEngine: Fix cairo_font_face_t leak + * Splash: Fix crash when softMask fails to allocate + * Remove incorrect \n and \r characters at the end of error() strings + +Release 24.08.0: + core: + * Add support for modifying the appearance stream text in form field choice + * Fix buffer overflow in Windows specific font selection code + * Fix crashes in malformed files + * Internal code improvements + + qt5: + * Add support for modifying the appearance stream text in form field choice + + qt6: + * Add support for modifying the appearance stream text in form field choice + + utils: + * pdfinfo: Fix crash in malformed documents + +Release 24.07.0: + core: + * Fix crashes in broken files + * Internal code improvements + + qt6: + * Add getters for document additional actions + * Implement reset forms link + + qt5: + * Add getters for document additional actions + * Implement reset forms link + + utils: + * pdfinfo: Fix crash in broken documents when using -dests + + build system: + * Mark glib-mkenums as required + +Release 24.06.0: + core: + * Performance improvements in some files + * Fix some issues with files bigger than 2^31 bytes + * Remove all cairo include guards for cairo < 1.16 + * Fix MSVC build + * Internal code improvements + + qt6: + * Update Qt6 doc example + * Use the non deprecated version of QString::fromUcs4 + + glib: + * properly document return value from poppler_font_info_scan + +Release 24.05.0: + core: + * Fix signing not being totally correct in some kind of PDF files + * Assume "Adobe-Identity" for character collection. Issue #1465 + * Small improvements in annotation font rendering + * Remove some GooString methods, use std::string ones instead + * Move some GooString methods to UTF.h + * Fix crash in broken files + + cpp: + * cpp: Fix crash extracting text and font in some files. Issue #1477 + * Change base class of ustring to char16_t + + qt6: + * Add async API for certificate validation + * Fix text extraction for Landscape/Seascape pages + + qt5: + * Add async API for certificate validation + * Fix text extraction for Landscape/Seascape pages + + utils: + * pdfdetach: Small code improvements + * pdftops: Write compliant ps header + + build system: + * Increase minimum supported base to that provided by Ubuntu 22.04 + +Release 24.04.0: + core: + * Optimize page text extraction speed + * Fix clipping path handling in some files. Issue #739 + * Fix regression in text selection + * Fix text search across lines between paragraphs + + qt6: + * Fix crash in SoundObject::data + + utils: + * pdfsig: Add Catalan translation + + build system: + * Build code as C++20 + +Release 24.03.0: + core: + * Fix opening some malformed files. Issue #1447 + * Skip drawing image when it has singular matrix. Issue #1114 + * Fix crash on malformed files + * Small internal code cleanup + + utils: + * pdfdetach: Fix potential directory traversal + * pdfimages: Enable to print filenames to stdout. + * pdfsig: Add visible name/date when signing an existing form signature field + +Release 24.02.0: + core: + * Fix reading some JBIG2 streams. Issue #1319 + * Fix saving some annotation interior color when it's empty + * Make searching for fonts when adding annotations a bit faster + * Make sure images are compressed when adding them + * Small internal code cleanup + + utils: + * pdfimages: return exit code 2 when error opening output files + +Release 24.01.0: + core: + * Don't crash on certain documents on the NSS signature backend + * Fix infinite loop in some annotation code if there's not space for even one character + * Fix build on Android with generic font configuration + * Small internal code cleanup + +Release 23.12.0: + core: + * Rewrite FoFiType1::parse to be more flexible. Issue #1422 + * Small internal code refactoring + +Release 23.11.0: + core: + * CairoOutputDev: Use internal downscaling algorithm if image exceeds Cairo's maximum dimensions. + * Internal code improvements + * Fix crash on malformed files + + utils: + * pdftocairo: Add option to document logical structure if output is pdf + * pdftocairo: EPS output should not contain %%PageOrientation + +Release 23.10.0: + core: + * cairo: update type 3 fonts for cairo 1.18 api + * Fix crash on malformed files + + build system: + * Make a few more dependencies soft-mandatory + * Add more supported gnupg releases + * Check if linker supports version scripts + +Release 23.09.0: + core: + * Add Android-specific font matching functionality + * Fix digital signatures for NeedAppearance=true + * Forms: Don't look up same glyph multiple times + * Provide the key location for certificates you can sign with + * Add ToUnicode support for similarequal + * Fix crash on malformed files + + qt5: + * Provide the key location for certificates you can sign with + * Allow to force a rasterized overprint preview during PS conversion + + qt6: + * Provide the key location for certificates you can sign with + * Allow to force a rasterized overprint preview during PS conversion + + pdfsig: + * Provide the key location for certificates you can sign with + +Release 23.08.0: + core: + * Fix GWG 19.2 - DeviceN Overprint (White) + * Splash: avoid bogus memory allocation size in doTilingPatternFill + * Fix use-of-uninitialized-value in XRef + * Fix float-cast-overflow error in Catalog + * Cleanup gpgme backend code + * Version symbols in poppler core + + glib: + * Improve poppler_get_available_signing_certificates + * Add new members to PopplerCertificateInfo + + utils: + * pdftotext: small improvement to man page + +Release 23.07.0: + core: + * Fix reading of utf8-with-bom files + * Fix crash if CERT_ExtractPublicKey doesn't return a public key + * Fix rendering of some malformed documents. Issue #1395 + * Allow for stream compression and compress font streams in forms + * Remove method Hints::getPageRanges + + qt5: + * Fix crash when overprint preview is enabled + * Don't fail signature basics tests if backend is not configured + + qt6: + * Fix crash when overprint preview is enabled + * Don't fail signature basics tests if backend is not configured + + utils: + * pdfsig: Allow showung and selecting signature backend + * pdfsig: Describe signature dump format in manual page + + glib: + * Add signing API + + build system: + * zlib is now mandatory + +Release 23.06.0: + core: + * CairoOutputDev: Fix crash when doing type3 rendering + * Fix crash with unknown signature hashing algorithms + * Add gpgme backend for signature handling + * Windows: Fix crash when signing existing signature + * FontInfo: Make it return proper information about font substitution + * FontInfo: Try harder to get Type 3 font name + * Store embedded fonts widths table in a more effective manner + * Skip font lookup for nonprintable characters + * Windows: Look for fonts in both windows font dir and poppler fonts dir + * Windows: symbol.ttf is not a good Symbol font + * Windows: Fix memory leak when looking for fonts + * Fix crash on malformed files + + qt5: + * Add API to allow selecting signature backend (nss or gpgme) + * Convert embedded files to bytearray a bit smarter + + qt6: + * Add API to allow selecting signature backend (nss or gpgme) + * Convert embedded files to bytearray a bit smarter + +Release 23.05.0: + core: + * Fix crash when filling some forms + * Set SigFlags when signing unsigned signature + * Add some infrastructure code to support multiple signing backends + * Fix potential stack overflow in PostScriptFunction::parseCode + * Fix some minor uninitialised memory reads + +Release 23.04.0: + core: + * Fix memory issue when signing fails. Issue #1372 + * Internal improvements of signature related code + * CairoOutputDev: improve type3 font rendering + * Fix memory leak in GlobalParams::findSystemFontFileForFamilyAndStyle + + utils: + * pdftocairo: Fix crash in some special situations + * pdfsig: allow holes in -dump signature list + * pdfsig: Support --help + +Release 23.03.0: + core: + * PngWriter: Fix potential uninitialized memory use + +Release 23.02.0: + core: + * CairoOutputDev: Fix rendering of color type 3 fonts + * CairoOutputDev: Add handling matte entry + * Fix segfault on wrong nssdir + * Fix "NSS could not shutdown" + + utils: + * pdfsig: Point out supports PKCS#11 URIs as nickname + +Release 23.01.0: + core: + * PDFDoc::sign: Fix crash if font can't be found + * PDFDoc::sign: Try Arial to sign if Helvetica isn't found + * FoFiType1::parse: Be more flexible parsing the encoding content. Issue #1324 + * Gfx::opBeginMarkedContent: Support Span with Name. Issue #1327 + * Splash: Avoid color issues due to implicit rounding + * Splash: Fix crash on malformed file. + * CairoOutputDev: Ignore text rendering mode for type3 fonts + * Remove unused FoFiType1::load function + + build system: + * Increase minimum required versions of several dependencies + * Improve include path handling + + qt6: + * Use less deprecated functions + Release 22.12.0: core: * Form::addFontToDefaultResources: Be stubborn in finding a font we can use. Issue #1272 diff --git a/README.contributors b/README.contributors index b310a5b..23fa13f 100644 --- a/README.contributors +++ b/README.contributors @@ -15,7 +15,8 @@ Channels of contact Poppler has three main channels of contact: * The poppler mailing list http://lists.freedesktop.org/mailman/listinfo/poppler * The poppler gitlab instance https://gitlab.freedesktop.org/poppler/poppler/ - * The #poppler channel at the IRC freenode network + * The #poppler channel at the IRC libera.chat network, which is also bridged + to [Matrix](https://matrix.to/#/#poppler:kde.org) Do not hesitate to drop by talk to people there. clang-format @@ -29,7 +30,7 @@ To get the clang-format warnings locally instead at CI time we recommend you to copy the hooks/pre-commit to your .git cp hooks/pre-commit .git/hooks/ -We are using clang-format 14 on CI. Unfortunately clang-format is not totally +We are using clang-format 19 on CI. Unfortunately clang-format is not totally compatible with older versions of itself. If CI gives you trouble but your local clang-format disagrees, just apply the changes suggested by CI and then commit with the --no-verify flag. If you get stuck, don't hesitate to ask the reviewer diff --git a/cmake/modules/FindCairo.cmake b/cmake/modules/FindCairo.cmake index d536c05..f0d1466 100644 --- a/cmake/modules/FindCairo.cmake +++ b/cmake/modules/FindCairo.cmake @@ -47,7 +47,7 @@ if(CAIRO_FOUND) HINTS ${_pc_cairo_INCLUDE_DIRS} PATH_SUFFIXES cairo ) - set(CAIRO_INCLUDE_DIRS "${CAIRO_INCLUDE_DIR}") + set(CAIRO_INCLUDE_DIRS "${CAIRO_INCLUDE_DIR};${_pc_cairo_INCLUDE_DIRS}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Cairo DEFAULT_MSG CAIRO_LIBRARIES CAIRO_INCLUDE_DIRS) diff --git a/cmake/modules/FindFontconfig.cmake b/cmake/modules/FindFontconfig.cmake deleted file mode 100644 index 375bf7a..0000000 --- a/cmake/modules/FindFontconfig.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# - Try to find the Fontconfig -# Once done this will define -# -# FONTCONFIG_FOUND - system has Fontconfig -# FONTCONFIG_INCLUDE_DIR - The include directory to use for the fontconfig headers -# FONTCONFIG_LIBRARIES - Link these to use FONTCONFIG -# FONTCONFIG_DEFINITIONS - Compiler switches required for using FONTCONFIG - -# Copyright (c) 2006,2007 Laurent Montel, -# -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. - -# NOTE: Once required cmake >=3.14, consider using built-in FindFontconfig -# See Poppler issue #955 for details - -if (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) - - # in cache already - set(FONTCONFIG_FOUND TRUE) - -else (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) - - if (NOT WIN32) - # use pkg-config to get the directories and then use these values - # in the FIND_PATH() and FIND_LIBRARY() calls - find_package(PkgConfig) - pkg_check_modules(PC_FONTCONFIG fontconfig) - - set(FONTCONFIG_DEFINITIONS ${PC_FONTCONFIG_CFLAGS_OTHER}) - endif (NOT WIN32) - - find_path(FONTCONFIG_INCLUDE_DIR fontconfig/fontconfig.h - PATHS - ${PC_FONTCONFIG_INCLUDEDIR} - ${PC_FONTCONFIG_INCLUDE_DIRS} - /usr/X11/include - ) - - find_library(FONTCONFIG_LIBRARIES NAMES fontconfig - PATHS - ${PC_FONTCONFIG_LIBDIR} - ${PC_FONTCONFIG_LIBRARY_DIRS} - ) - - include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Fontconfig DEFAULT_MSG FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR ) - - mark_as_advanced(FONTCONFIG_LIBRARIES FONTCONFIG_INCLUDE_DIR) - -endif (FONTCONFIG_LIBRARIES AND FONTCONFIG_INCLUDE_DIR) - diff --git a/cmake/modules/FindIconv.cmake b/cmake/modules/FindIconv.cmake deleted file mode 100644 index ab9ad56..0000000 --- a/cmake/modules/FindIconv.cmake +++ /dev/null @@ -1,64 +0,0 @@ -# - Try to find Iconv -# Once done this will define -# -# ICONV_FOUND - system has Iconv -# ICONV_INCLUDE_DIR - the Iconv include directory -# ICONV_LIBRARIES - Link these to use Iconv -# ICONV_SECOND_ARGUMENT_IS_CONST - the second argument for iconv() is const -# - -# NOTE: Once required CMake >=3.11, consider using built-in FindIconv -# See Poppler issue #955 for details - -include(CheckCXXSourceCompiles) - -IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) - # Already in cache, be silent - SET(ICONV_FIND_QUIETLY TRUE) -ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) - -FIND_PATH(ICONV_INCLUDE_DIR iconv.h) - -FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv libiconv-2 c) - -IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) - SET(ICONV_FOUND TRUE) -ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) - -set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) -set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) -IF(ICONV_FOUND) - check_cxx_source_compiles(" - #include - int main(){ - iconv_t conv = 0; - const char* in = 0; - size_t ilen = 0; - char* out = 0; - size_t olen = 0; - iconv(conv, &in, &ilen, &out, &olen); - return 0; - } -" ICONV_SECOND_ARGUMENT_IS_CONST ) - IF(ICONV_SECOND_ARGUMENT_IS_CONST) - SET(ICONV_CONST "const") - ENDIF(ICONV_SECOND_ARGUMENT_IS_CONST) -ENDIF(ICONV_FOUND) -set(CMAKE_REQUIRED_INCLUDES) -set(CMAKE_REQUIRED_LIBRARIES) - -IF(ICONV_FOUND) - IF(NOT ICONV_FIND_QUIETLY) - MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}") - ENDIF(NOT ICONV_FIND_QUIETLY) -ELSE(ICONV_FOUND) - IF(Iconv_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find Iconv") - ENDIF(Iconv_FIND_REQUIRED) -ENDIF(ICONV_FOUND) - -MARK_AS_ADVANCED( - ICONV_INCLUDE_DIR - ICONV_LIBRARIES - ICONV_SECOND_ARGUMENT_IS_CONST -) diff --git a/cmake/modules/FindNSS3.cmake b/cmake/modules/FindNSS3.cmake index 9945a68..d5214a7 100644 --- a/cmake/modules/FindNSS3.cmake +++ b/cmake/modules/FindNSS3.cmake @@ -1,13 +1,9 @@ # - try to find NSS3 libraries # Once done this will define # -# NSS_FOUND - system has NSS3 +# NSS3_FOUND - system has NSS3 # PkgConfig::NSS3 - Use this in target_link_libraries to bring both includes and link libraries # -# Deprecated, use the above variables: -# NSS3_CFLAGS - the NSS CFlags -# NSS3_LIBRARIES - Link these to use NSS -# # Copyright 2015 André Guerreiro, # Copyright 2022 Albert Astals Cid, # @@ -18,6 +14,6 @@ include(FindPackageHandleStandardArgs) find_package(PkgConfig REQUIRED) -pkg_check_modules(NSS3 IMPORTED_TARGET "nss>=3.19") +pkg_check_modules(NSS3 IMPORTED_TARGET "nss>=3.68") find_package_handle_standard_args(NSS3 DEFAULT_MSG NSS3_LIBRARIES NSS3_CFLAGS) diff --git a/config.h.cmake b/config.h.cmake index ea679a4..9a48ec1 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -18,15 +18,24 @@ /* Do not hardcode the library location */ #cmakedefine ENABLE_RELOCATABLE 1 -/* Build against zlib. */ -#cmakedefine ENABLE_ZLIB 1 - /* Use zlib instead of builtin zlib decoder to uncompress flate streams. */ #cmakedefine ENABLE_ZLIB_UNCOMPRESS 1 /* Build against libnss3 for digital signature validation */ #cmakedefine ENABLE_NSS3 1 +/* Build against libgpgme for digital signature validation */ +#cmakedefine ENABLE_GPGME 1 + +/* Enable pgp signatures in GPG backend by default */ +#cmakedefine ENABLE_PGP_SIGNATURES 1 + +/* Signatures enabled */ +#cmakedefine ENABLE_SIGNATURES 1 + +/* Default signature backend */ +#cmakedefine DEFAULT_SIGNATURE_BACKEND "${DEFAULT_SIGNATURE_BACKEND}" + /* Use cairo for rendering. */ #cmakedefine HAVE_CAIRO 1 @@ -159,6 +168,9 @@ /* Use win32 font configuration backend */ #cmakedefine WITH_FONTCONFIGURATION_WIN32 1 +/* Use android font configuration backend */ +#cmakedefine WITH_FONTCONFIGURATION_ANDROID 1 + /* OpenJPEG with the OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG flag */ #cmakedefine WITH_OPENJPEG_IGNORE_PCLR_CMAP_CDEF_FLAG 1 diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index a878faf..f275b9c 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,7 +1,6 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - ${ICONV_INCLUDE_DIR} ) configure_file(poppler-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h @ONLY) @@ -26,15 +25,15 @@ set(poppler_cpp_SRCS add_library(poppler-cpp ${poppler_cpp_SRCS}) generate_export_header(poppler-cpp BASE_NAME poppler-cpp EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/poppler_cpp_export.h") -set_target_properties(poppler-cpp PROPERTIES VERSION 0.11.0 SOVERSION 0) +set_target_properties(poppler-cpp PROPERTIES VERSION 2.0.0 SOVERSION 2) if(MINGW AND BUILD_SHARED_LIBS) get_target_property(POPPLER_CPP_SOVERSION poppler-cpp SOVERSION) set_target_properties(poppler-cpp PROPERTIES SUFFIX "-${POPPLER_CPP_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") endif() -target_link_libraries(poppler-cpp poppler ${ICONV_LIBRARIES}) +target_link_libraries(poppler-cpp poppler Iconv::Iconv) install(TARGETS poppler-cpp RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES +set(poppler_cpp_all_install_headers poppler-destination.h poppler-document.h poppler-embedded-file.h @@ -49,5 +48,17 @@ install(FILES poppler-toc.h ${CMAKE_CURRENT_BINARY_DIR}/poppler_cpp_export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h - DESTINATION include/poppler/cpp) +) +if (${CMAKE_VERSION} VERSION_LESS "3.23.0") + install(FILES ${poppler_cpp_all_install_headers} DESTINATION include/poppler/cpp) +else() + target_sources(poppler-cpp + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR} + FILES + ${poppler_cpp_all_install_headers} + ) + install(TARGETS poppler-cpp FILE_SET HEADERS DESTINATION include/poppler/cpp) +endif() diff --git a/cpp/Doxyfile b/cpp/Doxyfile index 2c6f0da..99f7050 100644 --- a/cpp/Doxyfile +++ b/cpp/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = "Poppler CPP" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 22.12.0 +PROJECT_NUMBER = 25.03.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/cpp/poppler-document-private.h b/cpp/poppler-document-private.h index 2f66220..cec01d0 100644 --- a/cpp/poppler-document-private.h +++ b/cpp/poppler-document-private.h @@ -2,6 +2,7 @@ * Copyright (C) 2009-2011, Pino Toscano * Copyright (C) 2018, 2020, 2022, Albert Astals Cid * Copyright (C) 2018, 2020, Adam Reichold + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +25,7 @@ #include "poppler-global.h" #include "poppler-config.h" -#include "GooString.h" +#include "goo/GooString.h" #include "PDFDoc.h" #include "GlobalParams.h" diff --git a/cpp/poppler-document.cpp b/cpp/poppler-document.cpp index 70cae1b..54dcf72 100644 --- a/cpp/poppler-document.cpp +++ b/cpp/poppler-document.cpp @@ -5,6 +5,7 @@ * Copyright (C) 2018, 2020, Adam Reichold * Copyright (C) 2019, Masamichi Hosoda * Copyright (C) 2019, 2020, Oliver Sander + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -165,7 +166,7 @@ bool document::unlock(const std::string &owner_password, const std::string &user { if (d->is_locked) { document_private *newdoc = nullptr; - if (d->doc_data.size() > 0) { + if (!d->doc_data.empty()) { newdoc = new document_private(&d->doc_data, owner_password, user_password); } else if (d->raw_doc_data) { newdoc = new document_private(d->raw_doc_data, d->raw_doc_data_length, owner_password, user_password); @@ -295,7 +296,7 @@ ustring document::info_key(const std::string &key) const } std::unique_ptr goo_value(d->doc->getDocInfoStringEntry(key.c_str())); - if (!goo_value.get()) { + if (!goo_value) { return ustring(); } @@ -314,15 +315,13 @@ bool document::set_info_key(const std::string &key, const ustring &val) return false; } - GooString *goo_val; + std::unique_ptr goo_val; - if (val.empty()) { - goo_val = nullptr; - } else { + if (!val.empty()) { goo_val = detail::ustring_to_unicode_GooString(val); } - d->doc->setDocInfoStringEntry(key.c_str(), goo_val); + d->doc->setDocInfoStringEntry(key.c_str(), std::move(goo_val)); return true; } @@ -340,7 +339,7 @@ time_type document::info_date(const std::string &key) const } std::unique_ptr goo_date(d->doc->getDocInfoStringEntry(key.c_str())); - if (!goo_date.get()) { + if (!goo_date) { return time_type(-1); } @@ -361,7 +360,7 @@ time_t document::info_date_t(const std::string &key) const } std::unique_ptr goo_date(d->doc->getDocInfoStringEntry(key.c_str())); - if (!goo_date.get()) { + if (!goo_date) { return time_t(-1); } @@ -381,16 +380,14 @@ bool document::set_info_date(const std::string &key, time_type val) return false; } - GooString *goo_date; + std::unique_ptr goo_date; - if (val == time_type(-1)) { - goo_date = nullptr; - } else { + if (val != time_type(-1)) { time_t t = static_cast(val); goo_date = timeToDateString(&t); } - d->doc->setDocInfoStringEntry(key.c_str(), goo_date); + d->doc->setDocInfoStringEntry(key.c_str(), std::move(goo_date)); return true; } @@ -407,15 +404,13 @@ bool document::set_info_date_t(const std::string &key, time_t val) return false; } - GooString *goo_date; + std::unique_ptr goo_date; - if (val == time_t(-1)) { - goo_date = nullptr; - } else { + if (val != time_t(-1)) { goo_date = timeToDateString(&val); } - d->doc->setDocInfoStringEntry(key.c_str(), goo_date); + d->doc->setDocInfoStringEntry(key.c_str(), std::move(goo_date)); return true; } @@ -432,7 +427,7 @@ ustring document::get_title() const } std::unique_ptr goo_title(d->doc->getDocInfoTitle()); - if (!goo_title.get()) { + if (!goo_title) { return ustring(); } @@ -451,15 +446,13 @@ bool document::set_title(const ustring &title) return false; } - GooString *goo_title; + std::unique_ptr goo_title; - if (title.empty()) { - goo_title = nullptr; - } else { + if (!title.empty()) { goo_title = detail::ustring_to_unicode_GooString(title); } - d->doc->setDocInfoTitle(goo_title); + d->doc->setDocInfoTitle(std::move(goo_title)); return true; } @@ -476,7 +469,7 @@ ustring document::get_author() const } std::unique_ptr goo_author(d->doc->getDocInfoAuthor()); - if (!goo_author.get()) { + if (!goo_author) { return ustring(); } @@ -495,15 +488,13 @@ bool document::set_author(const ustring &author) return false; } - GooString *goo_author; + std::unique_ptr goo_author; - if (author.empty()) { - goo_author = nullptr; - } else { + if (!author.empty()) { goo_author = detail::ustring_to_unicode_GooString(author); } - d->doc->setDocInfoAuthor(goo_author); + d->doc->setDocInfoAuthor(std::move(goo_author)); return true; } @@ -520,7 +511,7 @@ ustring document::get_subject() const } std::unique_ptr goo_subject(d->doc->getDocInfoSubject()); - if (!goo_subject.get()) { + if (!goo_subject) { return ustring(); } @@ -539,15 +530,13 @@ bool document::set_subject(const ustring &subject) return false; } - GooString *goo_subject; + std::unique_ptr goo_subject; - if (subject.empty()) { - goo_subject = nullptr; - } else { + if (!subject.empty()) { goo_subject = detail::ustring_to_unicode_GooString(subject); } - d->doc->setDocInfoSubject(goo_subject); + d->doc->setDocInfoSubject(std::move(goo_subject)); return true; } @@ -564,7 +553,7 @@ ustring document::get_keywords() const } std::unique_ptr goo_keywords(d->doc->getDocInfoKeywords()); - if (!goo_keywords.get()) { + if (!goo_keywords) { return ustring(); } @@ -583,15 +572,13 @@ bool document::set_keywords(const ustring &keywords) return false; } - GooString *goo_keywords; + std::unique_ptr goo_keywords; - if (keywords.empty()) { - goo_keywords = nullptr; - } else { + if (!keywords.empty()) { goo_keywords = detail::ustring_to_unicode_GooString(keywords); } - d->doc->setDocInfoKeywords(goo_keywords); + d->doc->setDocInfoKeywords(std::move(goo_keywords)); return true; } @@ -608,7 +595,7 @@ ustring document::get_creator() const } std::unique_ptr goo_creator(d->doc->getDocInfoCreator()); - if (!goo_creator.get()) { + if (!goo_creator) { return ustring(); } @@ -627,15 +614,13 @@ bool document::set_creator(const ustring &creator) return false; } - GooString *goo_creator; + std::unique_ptr goo_creator; - if (creator.empty()) { - goo_creator = nullptr; - } else { + if (!creator.empty()) { goo_creator = detail::ustring_to_unicode_GooString(creator); } - d->doc->setDocInfoCreator(goo_creator); + d->doc->setDocInfoCreator(std::move(goo_creator)); return true; } @@ -652,7 +637,7 @@ ustring document::get_producer() const } std::unique_ptr goo_producer(d->doc->getDocInfoProducer()); - if (!goo_producer.get()) { + if (!goo_producer) { return ustring(); } @@ -671,15 +656,13 @@ bool document::set_producer(const ustring &producer) return false; } - GooString *goo_producer; + std::unique_ptr goo_producer; - if (producer.empty()) { - goo_producer = nullptr; - } else { + if (!producer.empty()) { goo_producer = detail::ustring_to_unicode_GooString(producer); } - d->doc->setDocInfoProducer(goo_producer); + d->doc->setDocInfoProducer(std::move(goo_producer)); return true; } @@ -696,7 +679,7 @@ time_type document::get_creation_date() const } std::unique_ptr goo_creation_date(d->doc->getDocInfoCreatDate()); - if (!goo_creation_date.get()) { + if (!goo_creation_date) { return time_type(-1); } @@ -716,7 +699,7 @@ time_t document::get_creation_date_t() const } std::unique_ptr goo_creation_date(d->doc->getDocInfoCreatDate()); - if (!goo_creation_date.get()) { + if (!goo_creation_date) { return time_t(-1); } @@ -735,16 +718,14 @@ bool document::set_creation_date(time_type creation_date) return false; } - GooString *goo_creation_date; + std::unique_ptr goo_creation_date; - if (creation_date == time_type(-1)) { - goo_creation_date = nullptr; - } else { + if (creation_date != time_type(-1)) { time_t t = static_cast(creation_date); goo_creation_date = timeToDateString(&t); } - d->doc->setDocInfoCreatDate(goo_creation_date); + d->doc->setDocInfoCreatDate(std::move(goo_creation_date)); return true; } @@ -760,15 +741,13 @@ bool document::set_creation_date_t(time_t creation_date) return false; } - GooString *goo_creation_date; + std::unique_ptr goo_creation_date; - if (creation_date == time_t(-1)) { - goo_creation_date = nullptr; - } else { + if (creation_date != time_t(-1)) { goo_creation_date = timeToDateString(&creation_date); } - d->doc->setDocInfoCreatDate(goo_creation_date); + d->doc->setDocInfoCreatDate(std::move(goo_creation_date)); return true; } @@ -785,7 +764,7 @@ time_type document::get_modification_date() const } std::unique_ptr goo_modification_date(d->doc->getDocInfoModDate()); - if (!goo_modification_date.get()) { + if (!goo_modification_date) { return time_type(-1); } @@ -805,7 +784,7 @@ time_t document::get_modification_date_t() const } std::unique_ptr goo_modification_date(d->doc->getDocInfoModDate()); - if (!goo_modification_date.get()) { + if (!goo_modification_date) { return time_t(-1); } @@ -824,16 +803,14 @@ bool document::set_modification_date(time_type mod_date) return false; } - GooString *goo_mod_date; + std::unique_ptr goo_mod_date; - if (mod_date == time_type(-1)) { - goo_mod_date = nullptr; - } else { + if (mod_date != time_type(-1)) { time_t t = static_cast(mod_date); goo_mod_date = timeToDateString(&t); } - d->doc->setDocInfoModDate(goo_mod_date); + d->doc->setDocInfoModDate(std::move(goo_mod_date)); return true; } @@ -849,15 +826,13 @@ bool document::set_modification_date_t(time_t mod_date) return false; } - GooString *goo_mod_date; + std::unique_ptr goo_mod_date; - if (mod_date == time_t(-1)) { - goo_mod_date = nullptr; - } else { + if (mod_date != time_t(-1)) { goo_mod_date = timeToDateString(&mod_date); } - d->doc->setDocInfoModDate(goo_mod_date); + d->doc->setDocInfoModDate(std::move(goo_mod_date)); return true; } @@ -928,7 +903,7 @@ bool document::has_permission(permission_enum which) const ustring document::metadata() const { std::unique_ptr md(d->doc->getCatalog()->readMetadata()); - if (md.get()) { + if (md) { return detail::unicode_GooString_to_ustring(md.get()); } return ustring(); @@ -1028,7 +1003,7 @@ std::vector document::fonts() const font_iterator it(0, d); while (it.has_next()) { const std::vector l = it.next(); - std::copy(l.begin(), l.end(), std::back_inserter(result)); + std::ranges::copy(l, std::back_inserter(result)); } return result; } diff --git a/cpp/poppler-embedded-file.cpp b/cpp/poppler-embedded-file.cpp index d0b240a..e64aa69 100644 --- a/cpp/poppler-embedded-file.cpp +++ b/cpp/poppler-embedded-file.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2009-2011, Pino Toscano * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2020, 2022 Albert Astals Cid + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -183,7 +184,9 @@ byte_array embedded_file::data() const return byte_array(); } - stream->reset(); + if (!stream->reset()) { + return byte_array {}; + } byte_array ret(1024); size_t data_len = 0; int i; diff --git a/cpp/poppler-font-private.h b/cpp/poppler-font-private.h index ba9c6e0..bb3911c 100644 --- a/cpp/poppler-font-private.h +++ b/cpp/poppler-font-private.h @@ -2,7 +2,7 @@ * Copyright (C) 2009, Pino Toscano * Copyright (C) 2015, Tamas Szekeres * Copyright (C) 2020, Suzuki Toshiya - * Copyright (C) 2021, Albert Astals Cid + * Copyright (C) 2021, 2024, 2025, Albert Astals Cid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,11 +35,13 @@ class poppler::font_info_private font_info_private() : type(font_info::unknown), is_embedded(false), is_subset(false) { } explicit font_info_private(FontInfo *fi) : type((font_info::type_enum)fi->getType()), is_embedded(fi->getEmbedded()), is_subset(fi->getSubset()) { - if (fi->getName()) { - font_name = fi->getName()->c_str(); + const std::optional &fiName = fi->getName(); + if (fiName) { + font_name = *fiName; } - if (fi->getFile()) { - font_file = fi->getFile()->c_str(); + const std::optional &fiFile = fi->getFile(); + if (fiFile) { + font_file = *fiFile; } ref = fi->getRef(); @@ -60,7 +62,7 @@ class poppler::font_iterator_private { public: font_iterator_private(int start_page, document_private *dd) : font_info_scanner(dd->doc, start_page), total_pages(dd->doc->getNumPages()), current_page((std::max)(start_page, 0)) { } - ~font_iterator_private() { } + ~font_iterator_private() = default; FontInfoScanner font_info_scanner; int total_pages; diff --git a/cpp/poppler-global.cpp b/cpp/poppler-global.cpp index 9cfb1d7..04903a8 100644 --- a/cpp/poppler-global.cpp +++ b/cpp/poppler-global.cpp @@ -58,7 +58,10 @@ struct MiniIconv } MiniIconv(const MiniIconv &) = delete; MiniIconv &operator=(const MiniIconv &) = delete; - bool is_valid() const { return i_ != (iconv_t)-1; } + bool is_valid() const + { + return i_ != (iconv_t)-1; // NOLINT(performance-no-int-to-ptr) + } explicit operator iconv_t() const { return i_; } iconv_t i_; }; @@ -203,21 +206,19 @@ using namespace poppler; The case sensitivity. */ -noncopyable::noncopyable() { } - -noncopyable::~noncopyable() { } +noncopyable::noncopyable() = default; noncopyable &noncopyable::operator=(noncopyable &&other) noexcept = default; -ustring::ustring() { } +ustring::ustring() = default; ustring::ustring(size_type len, value_type ch) : std::basic_string(len, ch) { } -ustring::~ustring() { } +ustring::~ustring() = default; byte_array ustring::to_utf8() const { - if (!size()) { + if (empty()) { return byte_array(); } @@ -251,7 +252,7 @@ byte_array ustring::to_utf8() const std::string ustring::to_latin1() const { - if (!size()) { + if (empty()) { return std::string(); } diff --git a/cpp/poppler-global.h b/cpp/poppler-global.h index 782e075..037abdb 100644 --- a/cpp/poppler-global.h +++ b/cpp/poppler-global.h @@ -6,6 +6,7 @@ * Copyright (C) 2021, 2022, Albert Astals Cid * Copyright (C) 2022, Tobias C. Berner * Copyright (C) 2022, Oliver Sander + * Copyright (C) 2024, hugegameartgd@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +46,7 @@ class POPPLER_CPP_EXPORT noncopyable protected: noncopyable(); - ~noncopyable(); + ~noncopyable() = default; noncopyable &operator=(noncopyable &&other) noexcept; }; @@ -98,7 +99,7 @@ typedef unsigned int /* time_t */ time_type; # pragma warning(push) # pragma warning(disable : 4251) /* class 'A' needs to have dll interface for to be used by clients of class 'B'. */ #endif -class POPPLER_CPP_EXPORT ustring : public std::basic_string +class POPPLER_CPP_EXPORT ustring : public std::basic_string { public: ustring(); diff --git a/cpp/poppler-image.cpp b/cpp/poppler-image.cpp index 44739a8..0ab5685 100644 --- a/cpp/poppler-image.cpp +++ b/cpp/poppler-image.cpp @@ -1,10 +1,11 @@ /* * Copyright (C) 2010-2011, Pino Toscano * Copyright (C) 2013 Adrian Johnson - * Copyright (C) 2017-2019, 2021, Albert Astals Cid + * Copyright (C) 2017-2019, 2021, 2024, Albert Astals Cid * Copyright (C) 2017, Jeroen Ooms * Copyright (C) 2018, Zsombor Hollay-Horvath * Copyright (C) 2018, Adam Reichold + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,17 +30,17 @@ #include "poppler-image-private.h" #include -#include "ImgWriter.h" +#include "goo/ImgWriter.h" #if defined(ENABLE_LIBPNG) -# include "PNGWriter.h" +# include "goo/PNGWriter.h" #endif #if defined(ENABLE_LIBJPEG) -# include "JpegWriter.h" +# include "goo/JpegWriter.h" #endif #if defined(ENABLE_LIBTIFF) -# include "TiffWriter.h" +# include "goo/TiffWriter.h" #endif -#include "NetPBMWriter.h" +#include "goo/NetPBMWriter.h" #include #include @@ -300,23 +301,15 @@ const char *image::const_data() const } /** - Copy of a slice of the image. + Copies the image (i.e. \em detaches) - \param r the sub-area of this image to copy; if empty, the whole image is - copied - - \returns a new image representing the specified part of the current image + \returns a new image copied from the current image */ -image image::copy(const rect &r) const +image image::copy() const { - if (r.is_empty()) { - image img(*this); - img.detach(); - return img; - } - - // ### FIXME - return *this; + image img(*this); + img.detach(); + return img; } /** @@ -344,7 +337,7 @@ bool image::save(const std::string &file_name, const std::string &out_format, in } std::string fmt = out_format; - std::transform(fmt.begin(), fmt.end(), fmt.begin(), tolower); + std::ranges::transform(fmt, fmt.begin(), tolower); std::unique_ptr w; const int actual_dpi = dpi == -1 ? 75 : dpi; @@ -368,7 +361,7 @@ bool image::save(const std::string &file_name, const std::string &out_format, in else if (fmt == "pnm") { w = std::make_unique(pnm_format(d->format)); } - if (!w.get()) { + if (!w) { return false; } FILE *f = fopen(file_name.c_str(), "wb"); diff --git a/cpp/poppler-image.h b/cpp/poppler-image.h index 2b98916..e15e136 100644 --- a/cpp/poppler-image.h +++ b/cpp/poppler-image.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2010, Pino Toscano * Copyright (C) 2018, Zsombor Hollay-Horvath + * Copyright (C) 2024, Albert Astals Cid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,7 +55,7 @@ class POPPLER_CPP_EXPORT image const char *const_data() const; int bytes_per_row() const; - image copy(const rect &r = rect()) const; + image copy() const; bool save(const std::string &file_name, const std::string &out_format, int dpi = -1) const; diff --git a/cpp/poppler-page-renderer.cpp b/cpp/poppler-page-renderer.cpp index 4b07e76..e5f3ced 100644 --- a/cpp/poppler-page-renderer.cpp +++ b/cpp/poppler-page-renderer.cpp @@ -264,7 +264,7 @@ image page_renderer::render_page(const page *p, double xres, double yres, int x, SplashColorMode colorMode; SplashThinLineMode lineMode; - if (!d->conv_color_mode(d->image_format, colorMode) || !d->conv_line_mode(d->line_mode, lineMode)) { + if (!poppler::page_renderer_private::conv_color_mode(d->image_format, colorMode) || !poppler::page_renderer_private::conv_line_mode(d->line_mode, lineMode)) { return image(); } diff --git a/cpp/poppler-page.cpp b/cpp/poppler-page.cpp index 526d3ee..9a8b892 100644 --- a/cpp/poppler-page.cpp +++ b/cpp/poppler-page.cpp @@ -63,7 +63,6 @@ void page_private::init_font_info_cache() } font_info_cache_initialized = true; - return; } /** diff --git a/cpp/poppler-private.cpp b/cpp/poppler-private.cpp index 0189fc7..5e7d4e2 100644 --- a/cpp/poppler-private.cpp +++ b/cpp/poppler-private.cpp @@ -6,6 +6,8 @@ * Copyright (C) 2017-2019 Albert Astals Cid * Copyright (C) 2018 Suzuki Toshiya * Copyright (C) 2020 Adam Reichold + * Copyright (C) 2024 Oliver Sander + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +26,9 @@ #include "poppler-private.h" -#include "GooString.h" +#include "goo/GooString.h" #include "Page.h" +#include "UTF.h" #include #include @@ -63,8 +66,8 @@ ustring detail::unicode_GooString_to_ustring(const GooString *str) const char *data = str->c_str(); const int len = str->getLength(); - const bool is_unicodeLE = str->hasUnicodeMarkerLE(); - const bool is_unicode = str->hasUnicodeMarker() || is_unicodeLE; + const bool is_unicodeLE = hasUnicodeByteOrderMarkLE(str->toStr()); + const bool is_unicode = hasUnicodeByteOrderMark(str->toStr()) || is_unicodeLE; int i = is_unicode ? 2 : 0; ustring::size_type ret_len = len - i; if (is_unicode) { @@ -101,7 +104,7 @@ ustring detail::unicode_to_ustring(const Unicode *u, int length) return str; } -GooString *detail::ustring_to_unicode_GooString(const ustring &str) +std::unique_ptr detail::ustring_to_unicode_GooString(const ustring &str) { const size_t len = str.size() * 2 + 2; const ustring::value_type *me = str.data(); @@ -112,6 +115,5 @@ GooString *detail::ustring_to_unicode_GooString(const ustring &str) ba[i * 2 + 2] = ((*me >> 8) & 0xff); ba[i * 2 + 3] = (*me & 0xff); } - GooString *goo = new GooString(&ba[0], len); - return goo; + return std::make_unique(&ba[0], len); } diff --git a/cpp/poppler-private.h b/cpp/poppler-private.h index 6875a2a..8607659 100644 --- a/cpp/poppler-private.h +++ b/cpp/poppler-private.h @@ -5,8 +5,9 @@ * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2020, Suzuki Toshiya * Copyright (C) 2018, 2020 Adam Reichold - * Copyright (C) 2018, 2020 Albert Astals Cid + * Copyright (C) 2018, 2020, 2024 Albert Astals Cid * Copyright (C) 2018, Zsombor Hollay-Horvath + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,7 +39,7 @@ class GooString; class PDFRectangle; -#define PSTR(str) const_cast(str) +#define PSTR(str) (const_cast(str)) namespace poppler { @@ -52,7 +53,7 @@ rectf pdfrectangle_to_rectf(const PDFRectangle &pdfrect); ustring unicode_GooString_to_ustring(const GooString *str); ustring unicode_to_ustring(const Unicode *u, int length); -GooString *ustring_to_unicode_GooString(const ustring &str); +std::unique_ptr ustring_to_unicode_GooString(const ustring &str); } diff --git a/cpp/poppler-rectangle.cpp b/cpp/poppler-rectangle.cpp index d248a4f..4ad36f2 100644 --- a/cpp/poppler-rectangle.cpp +++ b/cpp/poppler-rectangle.cpp @@ -43,7 +43,7 @@ using namespace poppler; A rectangle with float (double) dimensions and coordinates. */ -std::ostream &poppler::operator<<(std::ostream &stream, const rect &r) +std::ostream &poppler::operator<<(std::ostream &stream, const rect &r) // clazy:exclude=function-args-by-value { stream << "[" << r.x() << "," << r.y() << " " << r.width() << "+" << r.height() << "]"; return stream; diff --git a/cpp/poppler-rectangle.h b/cpp/poppler-rectangle.h index 5657ec9..074252f 100644 --- a/cpp/poppler-rectangle.h +++ b/cpp/poppler-rectangle.h @@ -29,7 +29,7 @@ class rectangle public: rectangle() : x1(), y1(), x2(), y2() { } rectangle(T _x, T _y, T w, T h) : x1(_x), y1(_y), x2(x1 + w), y2(y1 + h) { } - ~rectangle() { } + ~rectangle() = default; bool is_empty() const { return (x1 == x2) && (y1 == y2); } diff --git a/cpp/poppler-toc.cpp b/cpp/poppler-toc.cpp index c79abde..d4eaa3b 100644 --- a/cpp/poppler-toc.cpp +++ b/cpp/poppler-toc.cpp @@ -1,7 +1,8 @@ /* * Copyright (C) 2009-2010, Pino Toscano - * Copyright (C) 2018, Albert Astals Cid + * Copyright (C) 2018, 2024, Albert Astals Cid * Copyright (C) 2019, Oliver Sander + * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,9 +31,9 @@ using namespace poppler; -toc_private::toc_private() { } +toc_private::toc_private() = default; -toc_private::~toc_private() { } +toc_private::~toc_private() = default; toc *toc_private::load_from_outline(Outline *outline) { @@ -41,7 +42,7 @@ toc *toc_private::load_from_outline(Outline *outline) } const std::vector *items = outline->getItems(); - if (!items || items->size() < 1) { + if (!items || items->empty()) { return nullptr; } @@ -61,9 +62,8 @@ toc_item_private::~toc_item_private() void toc_item_private::load(const OutlineItem *item) { - const Unicode *title_unicode = item->getTitle(); - const int title_length = item->getTitleLength(); - title = detail::unicode_to_ustring(title_unicode, title_length); + const std::vector &title_unicode = item->getTitle(); + title = detail::unicode_to_ustring(title_unicode.data(), title_unicode.size()); is_open = item->isOpen(); } diff --git a/cpp/tests/poppler-dump.cpp b/cpp/tests/poppler-dump.cpp index c10b8f4..da338a8 100644 --- a/cpp/tests/poppler-dump.cpp +++ b/cpp/tests/poppler-dump.cpp @@ -452,7 +452,7 @@ int main(int argc, char *argv[]) std::string file_name(argv[1]); std::unique_ptr doc(poppler::document::load_from_file(file_name)); - if (!doc.get()) { + if (!doc) { error("loading error"); } if (doc->is_locked()) { diff --git a/cpp/tests/poppler-render.cpp b/cpp/tests/poppler-render.cpp index f03cad7..bab1e98 100644 --- a/cpp/tests/poppler-render.cpp +++ b/cpp/tests/poppler-render.cpp @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) const std::string file_name(argv[1]); std::unique_ptr doc(poppler::document::load_from_file(file_name)); - if (!doc.get()) { + if (!doc) { error("loading error"); } if (doc->is_locked()) { @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) error("specified page number out of page count"); } std::unique_ptr p(doc->create_page(doc_page)); - if (!p.get()) { + if (!p) { error("NULL page"); } diff --git a/debian/changelog b/debian/changelog index 85bd7aa..e99f046 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,22 +1,259 @@ -poppler (22.12.0-2+deb12u1deepin1) unstable; urgency=medium +poppler (25.03.0-5+deb13u2deepin1) unstable; urgency=medium - * fix CVE-2024-6239 + * Revert 64-bit time_t changes. - -- zengwei Thu, 29 Jan 2026 13:43:35 +0800 + -- tunaichao Mon, 16 Mar 2026 11:19:15 +0800 -poppler (22.12.0-2+deb12u1) bookworm; urgency=medium +poppler (25.03.0-5+deb13u2) trixie; urgency=high + + [ Leonidas Da Silva Barbosa ] + * SECURITY UPDATE: Denial of service + - debian/patches/CVE-2025-50420.patch: don't continue + recursing in PDFDoc in poppler/PDFDoc.cc. + - CVE-2025-50420 (Closes: #1110463) + + -- Jeremy Bícha Sun, 31 Aug 2025 14:01:36 -0400 + +poppler (25.03.0-5) unstable; urgency=high + + [ Marc Deslauriers ] + * SECURITY UPDATE: DoS via reference count overflow + - debian/patches/CVE-2025-52886.patch: limit amount of annots per + document/page in poppler/Annot.cc, poppler/Page.cc. + - CVE-2025-52886 (Closes: #1108784) + + -- Jeremy Bícha Mon, 28 Jul 2025 10:55:12 +0200 + +poppler (25.03.0-4) unstable; urgency=high + + * Team upload + * SECURITY UPDATE: Properly verify abde.pkcs7.sha1 signatures + - Cherry-pick upstream fix for the + NSSSignatureVerification::validateSignature function + in NSSCryptoSignBackend.cc + - CVE-2025-43903 (Closes: #1103545) + + -- Jeremy Bícha Fri, 18 Apr 2025 18:16:32 -0400 + +poppler (25.03.0-3) unstable; urgency=high + + * Team upload + * SECURITY UPDATE: floating-point exception vulnerability (Closes: #1102190) + - Cherry-pick upstream fix for the PSStack::roll function + in Function.cc + - CVE-2025-32364 + * SECURITY UPDATE: out-of-bounds read vulnerability (Closes: #1102191) + - Cherry-pick upstream fix for the JBIG2Bitmap::combine function + in JBIG2Stream.cc (LP: #2106404) + - CVE-2025-32365 + + -- Jeremy Bícha Mon, 07 Apr 2025 11:11:10 -0400 + +poppler (25.03.0-2) unstable; urgency=medium + + * Team upload + * Release to Unstable (LP: #2100905) + + -- Jeremy Bícha Thu, 06 Mar 2025 07:13:50 -0500 + +poppler (25.03.0-1) experimental; urgency=medium + + * Team upload + * New upstream release + * Rename libpoppler145 to libpoppler147 to match soname bump + * Update symbols files + * Remove all patches: applied in new release + + -- Jeremy Bícha Tue, 04 Mar 2025 16:22:49 -0500 + +poppler (25.01.0-5) unstable; urgency=medium + + * Team upload + * Cherry-pick default signature colors fix for Papers (Closes: #1098406) + + -- Jeremy Bícha Thu, 27 Feb 2025 15:01:21 -0500 + +poppler (25.01.0-4) unstable; urgency=medium + + * Add patch to fix glib crash, needed by papers 48 + + -- Jeremy Bícha Thu, 06 Feb 2025 22:31:49 -0500 + +poppler (25.01.0-3) unstable; urgency=medium + + [ Helmut Grohne ] + * Fix FTCBFS: Disable gtkdoc in arch-only build (Closes: #1060865) + + -- Jeremy Bícha Mon, 03 Feb 2025 09:19:28 -0500 + +poppler (25.01.0-2) unstable; urgency=medium + + * Team upload + * Run wrap-and-sort + * Update gir Depends + * Remove obsolete Lintian overrides + * Release to unstable + + -- Jeremy Bícha Sun, 26 Jan 2025 13:53:06 -0500 + +poppler (25.01.0-1) experimental; urgency=medium + + * Team upload + * New upstream release + * Rename libpoppler and libpoppler-cpp packages for soname bump + * Update symbol files with new symbols + * Remove remaining patch: applied upstream + + -- Alessandro Astone Wed, 15 Jan 2025 12:29:05 +0100 + +poppler (24.08.0-4) unstable; urgency=high + + * Team upload + * SECURITY UPDATE: out-of-bounds read vulnerability (Closes: #1091322) + - Cherry-pick upstream fix for the JBIG2Bitmap::combine function + in JBIG2Stream.cc + - CVE-2024-56378 + * libpoppler-glib-dev: Add Provides: ${gir:Provides} + + -- Jeremy Bícha Sat, 04 Jan 2025 17:16:42 -0900 + +poppler (24.08.0-3) unstable; urgency=medium + + [ John Scott ] + * Enable the GnuPG/gpgsm PDF signature backend. + + -- Emilio Pozuelo Monfort Wed, 23 Oct 2024 09:35:17 +0200 + +poppler (24.08.0-2) unstable; urgency=medium + + * Team upload + * Release to unstable + + -- Jeremy Bícha Sun, 18 Aug 2024 10:46:36 -0400 + +poppler (24.08.0-1) experimental; urgency=medium + + * Team upload + + [ Jeremy Bícha ] + * New upstream release + * Update library name to libpoppler140 to match soname bump + * debian/libpoppler-qt*.symbols.in: Add new symbols + + [ Dandan Zhang ] + * Build qt6 library for loong64 (Closes: #1074410) + + -- Jeremy Bícha Thu, 01 Aug 2024 19:46:39 -0400 + +poppler (24.06.0-2) experimental; urgency=medium + + * Update symbols files + + -- Jeremy Bícha Thu, 20 Jun 2024 09:03:23 -0400 + +poppler (24.06.0-1) experimental; urgency=medium + + * Team upload + * New upstream release + * Update library names to libpoppler134 & libpoppler-cpp1 for soname bumps + * debian/poppler-utils.install: Install pdfsig translation + * Bump Standards Version to 4.7.0 + + -- Jeremy Bícha Tue, 04 Jun 2024 11:17:27 -0400 + +poppler (24.02.0-5) unstable; urgency=medium + + * Team upload + * Stop building Qt6 library on i386 + + -- Jeremy Bícha Thu, 16 May 2024 10:25:19 +0200 + +poppler (24.02.0-4) unstable; urgency=medium + + * Team upload + * Fix error in declaring Replaces: libpoppler-cpp0v5 (Closes: #1071009) + + -- Jeremy Bícha Mon, 13 May 2024 13:32:22 +0200 + +poppler (24.02.0-3) unstable; urgency=medium + + * Team upload + * Restore t64 changes from 22.12.0-2.2 and bump Breaks/Replaces. + Use standard versioned Replaces instead of unversioned. (Closes: #1070862) + + -- Jeremy Bícha Fri, 10 May 2024 14:18:27 -0400 + +poppler (24.02.0-2) unstable; urgency=medium + + * Team upload + * Release to unstable + + -- Jeremy Bícha Fri, 10 May 2024 05:43:01 -0400 + +poppler (24.02.0-1) experimental; urgency=medium + + * Team upload + * New upstream release + * Update library name to libpoppler134 to match soname + * debian/copyright: Update path of a moved file + + -- Amin Bandali Tue, 13 Feb 2024 11:59:19 -0500 + +poppler (23.12.0-1) experimental; urgency=medium + + * Team upload + * New upstream release + * Update library name to libpoppler133 to match soname + * Add new symbols to symbols files + * Build-Depend on libcurl4-gnutls-dev + + -- Jeremy Bícha Fri, 01 Dec 2023 10:08:39 -0500 + +poppler (23.08.0-2) experimental; urgency=medium + + * Team upload + + [ Jeremy Bicha ] + * Explicitly disable gpg support for now + + [ Amin Bandali ] + * 23.08.0 upstream release addresses CVE-2023-34872 (Closes: #1042811) + * debian/rules: Opt into LTO (to minimize symbols diff with Ubuntu) + * Drop most optional symbols from libpoppler-qt5-1 and libpoppler-qt6-3 + + -- Amin Bandali Wed, 16 Aug 2023 10:20:27 -0400 + +poppler (23.08.0-1) experimental; urgency=medium + + * Team upload + * New upstream release (Closes: #969907) + * Update library name to libpoppler130 to match soname + * Address some lintian error and warnings: + - E: libpoppler-private-dev: depends-on-obsolete-package + Suggests: libfreetype6-dev => libfreetype-dev + - W: poppler source: build-depends-on-obsolete-package + Build-Depends: libfontconfig1-dev => libfontconfig-dev + - W: poppler source: build-depends-on-obsolete-package + Build-Depends: libfreetype6-dev => libfreetype-dev + * Update standards version to 4.6.2, no changes needed + * Update minimum version of several Build-Depends and Depends + per upstream + + -- Amin Bandali Thu, 10 Aug 2023 17:27:57 -0400 + +poppler (22.12.0-2.2) unstable; urgency=medium + + * Non-maintainer upload. + * Fix wrong extraneous X-Time64-Compat declarations. Closes: #1065155. + + -- Steve Langasek Sun, 03 Mar 2024 08:43:35 +0000 + +poppler (22.12.0-2.1) unstable; urgency=medium * Non-maintainer upload. - * CVE-2023-34872: OutlineItem::open crash on malformed files - (Closes: #1042811) - * CVE-2024-56378: Out-of-bounds read in JBIG2Bitmap::combine - (Closes: #1091322) - * CVE-2025-32364: Floating point exception in PSStack::roll - (Closes: #1102190) - * CVE-2025-32365: Out-of-bounds read in JBIG2:Bitmap::combine - (Closes: #1102191) - - -- Adrian Bunk Sat, 12 Apr 2025 21:26:36 +0300 + * Rename libraries for 64-bit time_t transition. Closes: #1064282 + + -- Benjamin Drung Thu, 29 Feb 2024 21:10:54 +0000 poppler (22.12.0-2) unstable; urgency=medium diff --git a/debian/control b/debian/control index e4b56e6..d924e93 100644 --- a/debian/control +++ b/debian/control @@ -4,45 +4,51 @@ Priority: optional Maintainer: Debian freedesktop.org maintainers Uploaders: Loic Minier , Pino Toscano , - Emilio Pozuelo Monfort , + Emilio Pozuelo Monfort Build-Depends: debhelper-compat (= 13), dh-sequence-gir, + dpkg-dev (>= 1.22.5), cmake, - libboost-dev (>= 1.58.0), - libglib2.0-dev (>= 2.56), - libfontconfig1-dev, - libcairo2-dev (>= 1.10.0), + gir1.2-cairo-1.0-dev, + gir1.2-gio-2.0-dev, + gir1.2-gobject-2.0-dev, + gtk-doc-tools (>= 1.14), + libboost-dev (>= 1.71.0), + libcairo2-dev (>= 1.16.0), + libcurl4-gnutls-dev, + libfontconfig-dev, + libfreetype-dev, libgdk-pixbuf-2.0-dev, + libgirepository1.0-dev (>= 1.64.0), + libglib2.0-dev (>= 2.64), + libgpgme-dev, + libgpgmepp-dev (>= 1.19.0), libgtk-3-dev, - libopenjp2-7-dev, libjpeg-dev, + liblcms2-dev, + libnss3-dev, + libopenjp2-7-dev, libpng-dev, libtiff-dev, - liblcms2-dev, - libfreetype6-dev, - gtk-doc-tools (>= 1.14), - pkg-config (>= 0.18), + pkgconf, python3:any, - libgirepository1.0-dev (>= 1.42.0-2~), + qt6-base-dev [amd64 arm64 armel armhf loong64 mips64el ppc64el riscv64 s390x alpha hppa hurd-any m68k powerpc ppc64 sh4 sparc64], qtbase5-dev, - qt6-base-dev, - zlib1g-dev, - libnss3-dev, -Build-Depends-Indep: libglib2.0-doc, - libcairo2-doc -Standards-Version: 4.6.1 + zlib1g-dev +Build-Depends-Indep: libcairo2-doc, libglib2.0-doc +Standards-Version: 4.7.0 Rules-Requires-Root: no Homepage: https://poppler.freedesktop.org/ Vcs-Git: https://salsa.debian.org/freedesktop-team/poppler.git Vcs-Browser: https://salsa.debian.org/freedesktop-team/poppler -Package: libpoppler126 +Package: libpoppler147 Architecture: any Section: libs Multi-Arch: same -Depends: ${shlibs:Depends}, - ${misc:Depends} +Depends: ${misc:Depends}, ${shlibs:Depends} Recommends: poppler-data +Suggests: gpgsm (>= 2.2.42) Description: PDF rendering library Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -52,8 +58,7 @@ Package: libpoppler-dev Architecture: any Section: libdevel Multi-Arch: same -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, +Depends: libpoppler147 (= ${binary:Version}), ${misc:Depends} Description: PDF rendering library -- development files Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -65,9 +70,8 @@ Package: libpoppler-private-dev Architecture: any Section: libdevel Multi-Arch: same -Depends: libpoppler-dev (= ${binary:Version}), - ${misc:Depends} -Suggests: libfreetype6-dev, +Depends: libpoppler-dev (= ${binary:Version}), ${misc:Depends} +Suggests: libfreetype-dev Description: PDF rendering library -- private development files Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -78,9 +82,9 @@ Package: libpoppler-glib8 Architecture: any Section: libs Multi-Arch: same -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends} +Depends: libpoppler147 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Description: PDF rendering library (GLib-based shared library) Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -90,13 +94,13 @@ Description: PDF rendering library (GLib-based shared library) Package: libpoppler-glib-dev Architecture: any Section: libdevel -Depends: libpoppler-glib8 (= ${binary:Version}), +Depends: libcairo2-dev (>= 1.16.0), + libglib2.0-dev (>= 2.64), libpoppler-dev (= ${binary:Version}), - gir1.2-poppler-0.18 (= ${binary:Version}), - libglib2.0-dev (>= 2.41), - libcairo2-dev (>= 1.10.0), - ${shlibs:Depends}, + libpoppler-glib8 (= ${binary:Version}), + ${gir:Depends}, ${misc:Depends} +Provides: ${gir:Provides} Suggests: libpoppler-glib-doc Description: PDF rendering library -- development files (GLib interface) Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -108,8 +112,7 @@ Package: libpoppler-glib-doc Architecture: all Section: doc Depends: ${misc:Depends} -Recommends: libglib2.0-doc, - libcairo2-doc, +Recommends: libcairo2-doc, libglib2.0-doc Multi-Arch: foreign Description: PDF rendering library -- documentation for the GLib interface Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -121,9 +124,7 @@ Package: gir1.2-poppler-0.18 Architecture: any Section: introspection Multi-Arch: same -Depends: ${shlibs:Depends}, - ${misc:Depends}, - ${gir:Depends} +Depends: ${gir:Depends}, ${misc:Depends} Description: GObject introspection data for poppler-glib Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -136,9 +137,9 @@ Package: libpoppler-qt5-1 Architecture: any Section: libs Multi-Arch: same -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends} +Depends: libpoppler147 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Description: PDF rendering library (Qt 5 based shared library) Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -149,10 +150,9 @@ Package: libpoppler-qt5-dev Architecture: any Section: libdevel Multi-Arch: same -Depends: libpoppler-qt5-1 (= ${binary:Version}), - libpoppler-dev (= ${binary:Version}), +Depends: libpoppler-dev (= ${binary:Version}), + libpoppler-qt5-1 (= ${binary:Version}), qtbase5-dev, - ${shlibs:Depends}, ${misc:Depends} Description: PDF rendering library -- development files (Qt 5 interface) Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -161,12 +161,12 @@ Description: PDF rendering library -- development files (Qt 5 interface) build applications using the Qt 5-based Poppler interface. Package: libpoppler-qt6-3 -Architecture: any +Architecture: hurd-any alpha amd64 arm64 armel armhf hppa loong64 m68k mips64el powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64 Section: libs Multi-Arch: same -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends} +Depends: libpoppler147 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Description: PDF rendering library (Qt 6 based shared library) Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -174,13 +174,12 @@ Description: PDF rendering library (Qt 6 based shared library) using the Qt 6 interface to Poppler. Package: libpoppler-qt6-dev -Architecture: any +Architecture: hurd-any alpha amd64 arm64 armel armhf hppa loong64 m68k mips64el powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64 Section: libdevel Multi-Arch: same -Depends: libpoppler-qt6-3 (= ${binary:Version}), - libpoppler-dev (= ${binary:Version}), +Depends: libpoppler-dev (= ${binary:Version}), + libpoppler-qt6-3 (= ${binary:Version}), qt6-base-dev, - ${shlibs:Depends}, ${misc:Depends} Description: PDF rendering library -- development files (Qt 6 interface) Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -188,15 +187,13 @@ Description: PDF rendering library -- development files (Qt 6 interface) This package contains the headers and development libraries needed to build applications using the Qt 6-based Poppler interface. -Package: libpoppler-cpp0v5 +Package: libpoppler-cpp2 Architecture: any Section: libs Multi-Arch: same -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends} -Conflicts: libpoppler-cpp0 -Replaces: libpoppler-cpp0 +Depends: libpoppler147 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Description: PDF rendering library (CPP shared library) Poppler is a PDF rendering library based on Xpdf PDF viewer. . @@ -207,9 +204,8 @@ Package: libpoppler-cpp-dev Architecture: any Section: libdevel Multi-Arch: same -Depends: libpoppler-cpp0v5 (= ${binary:Version}), +Depends: libpoppler-cpp2 (= ${binary:Version}), libpoppler-dev (= ${binary:Version}), - ${shlibs:Depends}, ${misc:Depends} Description: PDF rendering library -- development files (CPP interface) Poppler is a PDF rendering library based on Xpdf PDF viewer. @@ -221,16 +217,13 @@ Package: poppler-utils Architecture: any Section: utils Multi-Arch: foreign -Depends: libpoppler126 (= ${binary:Version}), - ${shlibs:Depends}, - ${misc:Depends} +Depends: libpoppler147 (= ${binary:Version}), + ${misc:Depends}, + ${shlibs:Depends} Breaks: xpdf-utils (<< 3.02-2~) Conflicts: pdftohtml -Replaces: xpdf-utils (<< 3.02-2~), - pdftohtml, - xpdf-reader -Provides: xpdf-utils, - pdftohtml +Replaces: pdftohtml, xpdf-reader, xpdf-utils (<< 3.02-2~) +Provides: pdftohtml, xpdf-utils Description: PDF utilities (based on Poppler) Poppler is a PDF rendering library based on Xpdf PDF viewer. . diff --git a/debian/copyright b/debian/copyright index 125b1b1..46f94ce 100644 --- a/debian/copyright +++ b/debian/copyright @@ -265,6 +265,24 @@ License: GPL-2 or GPL-3 of the poppler core library (libpoppler.so.X) is GPL v2 or GPL v3. All the Poppler contributions are licensed as GPL v2+. +Files: cpp/tests/fuzzing/pdf_fuzzer.cc +Copyright: Copyright 2018 Google Inc. +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the complete text of the Apache + License can be found in `/usr/share/common-licenses/Apache-2.0'. + License: GPL-2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -297,21 +315,3 @@ License: GPL-3 . On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-3'. - -Files: cpp/tests/pdf_fuzzer.cc -Copyright: Copyright 2018 Google Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the complete text of the Apache - License can be found in `/usr/share/common-licenses/Apache-2.0'. diff --git a/debian/gbp.conf b/debian/gbp.conf index 4d4f9c2..d99bf4a 100644 --- a/debian/gbp.conf +++ b/debian/gbp.conf @@ -1,3 +1,3 @@ [DEFAULT] -debian-branch = master +debian-branch = trixie pristine-tar = false diff --git a/debian/libpoppler-cpp0v5.install b/debian/libpoppler-cpp0v5.install deleted file mode 100644 index 6f925b7..0000000 --- a/debian/libpoppler-cpp0v5.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/libpoppler-cpp.so.0* diff --git a/debian/libpoppler-cpp2.install b/debian/libpoppler-cpp2.install new file mode 100644 index 0000000..e0c97de --- /dev/null +++ b/debian/libpoppler-cpp2.install @@ -0,0 +1 @@ +usr/lib/*/libpoppler-cpp.so.2* diff --git a/debian/libpoppler-glib8.symbols.in b/debian/libpoppler-glib8.symbols.in index 2a251fe..616a420 100644 --- a/debian/libpoppler-glib8.symbols.in +++ b/debian/libpoppler-glib8.symbols.in @@ -22,10 +22,16 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_annot_file_attachment_get_type@Base 0.18.0 poppler_annot_flag_get_type@Base 0.18.0 poppler_annot_free_text_get_callout_line@Base 0.18.0 + poppler_annot_free_text_get_font_color@Base 25.01.0 + poppler_annot_free_text_get_font_desc@Base 25.01.0 poppler_annot_free_text_get_quadding@Base 0.18.0 poppler_annot_free_text_get_type@Base 0.18.0 + poppler_annot_free_text_new@Base 25.01.0 poppler_annot_free_text_quadding_get_type@Base 0.18.0 + poppler_annot_free_text_set_font_color@Base 25.01.0 + poppler_annot_free_text_set_font_desc@Base 25.01.0 poppler_annot_get_annot_type@Base 0.18.0 + poppler_annot_get_border_width@Base 25.01.0 poppler_annot_get_color@Base 0.18.0 poppler_annot_get_contents@Base 0.18.0 poppler_annot_get_flags@Base 0.18.0 @@ -62,6 +68,7 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_annot_movie_get_type@Base 0.18.0 poppler_annot_screen_get_action@Base 0.18.0 poppler_annot_screen_get_type@Base 0.18.0 + poppler_annot_set_border_width@Base 25.01.0 poppler_annot_set_color@Base 0.18.0 poppler_annot_set_contents@Base 0.18.0 poppler_annot_set_flags@Base 0.22.1 @@ -103,6 +110,18 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_attachment_save_to_callback@Base 0.18.0 poppler_attachment_save_to_fd@Base 22.02.0 poppler_backend_get_type@Base 0.18.0 + poppler_certificate_info_copy@Base 23.08.0 + poppler_certificate_info_free@Base 23.08.0 + poppler_certificate_info_get_expiration_time@Base 23.08.0 + poppler_certificate_info_get_id@Base 23.08.0 + poppler_certificate_info_get_issuance_time@Base 23.08.0 + poppler_certificate_info_get_issuer_common_name@Base 23.08.0 + poppler_certificate_info_get_issuer_email@Base 23.08.0 + poppler_certificate_info_get_issuer_organization@Base 23.08.0 + poppler_certificate_info_get_subject_common_name@Base 23.08.0 + poppler_certificate_info_get_subject_email@Base 23.08.0 + poppler_certificate_info_get_subject_organization@Base 23.08.0 + poppler_certificate_info_get_type@Base 23.08.0 poppler_certificate_status_get_type@Base 22.02.0 poppler_color_copy@Base 0.18.0 poppler_color_free@Base 0.18.0 @@ -172,9 +191,15 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_document_set_producer@Base 0.46.0 poppler_document_set_subject@Base 0.46.0 poppler_document_set_title@Base 0.46.0 + poppler_document_sign@Base 23.08.0 + poppler_document_sign_finish@Base 23.08.0 poppler_error_get_type@Base 0.18.0 poppler_error_quark@Base 0.18.0 poppler_find_flags_get_type@Base 0.22.1 + poppler_font_description_copy@Base 25.01.0 + poppler_font_description_free@Base 25.01.0 + poppler_font_description_get_type@Base 25.01.0 + poppler_font_description_new@Base 25.01.0 poppler_font_info_free@Base 0.18.0 poppler_font_info_get_type@Base 0.18.0 poppler_font_info_new@Base 0.18.0 @@ -238,7 +263,10 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_form_field_text_set_text@Base 0.18.0 poppler_form_field_type_get_type@Base 0.18.0 poppler_form_text_type_get_type@Base 0.18.0 + poppler_get_available_signing_certificates@Base 23.08.0 poppler_get_backend@Base 0.18.0 + poppler_get_certificate_info_by_id@Base 23.08.0 + poppler_get_nss_dir@Base 23.08.0 poppler_get_version@Base 0.18.0 poppler_image_mapping_copy@Base 0.18.0 poppler_image_mapping_free@Base 0.18.0 @@ -334,6 +362,7 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_page_render@Base 0.18.0 poppler_page_render_for_printing@Base 0.18.0 poppler_page_render_for_printing_with_options@Base 0.18.0 + poppler_page_render_full@Base 25.03.0 poppler_page_render_selection@Base 0.18.0 poppler_page_render_to_ps@Base 0.18.0 poppler_page_selection_region_free@Base 0.18.0 @@ -371,9 +400,13 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_rectangle_free@Base 0.18.0 poppler_rectangle_get_type@Base 0.18.0 poppler_rectangle_new@Base 0.18.0 + poppler_render_annots_flags_get_type@Base 25.03.0 poppler_selection_style_get_type@Base 0.18.0 + poppler_set_nss_dir@Base 23.08.0 + poppler_set_nss_password_callback@Base 23.08.0 poppler_signature_info_copy@Base 22.02.0 poppler_signature_info_free@Base 22.02.0 + poppler_signature_info_get_certificate_info@Base 23.08.0 poppler_signature_info_get_certificate_status@Base 22.02.0 poppler_signature_info_get_local_signing_time@Base 22.02.0 poppler_signature_info_get_signature_status@Base 22.02.0 @@ -381,6 +414,49 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_signature_info_get_type@Base 22.02.0 poppler_signature_status_get_type@Base 22.02.0 poppler_signature_validation_flags_get_type@Base 22.02.0 + poppler_signing_data_copy@Base 23.08.0 + poppler_signing_data_free@Base 23.08.0 + poppler_signing_data_get_background_color@Base 23.08.0 + poppler_signing_data_get_border_color@Base 23.08.0 + poppler_signing_data_get_border_width@Base 23.08.0 + poppler_signing_data_get_certificate_info@Base 23.08.0 + poppler_signing_data_get_destination_filename@Base 23.08.0 + poppler_signing_data_get_document_owner_password@Base 23.08.0 + poppler_signing_data_get_document_user_password@Base 23.08.0 + poppler_signing_data_get_field_partial_name@Base 23.08.0 + poppler_signing_data_get_font_color@Base 23.08.0 + poppler_signing_data_get_font_size@Base 23.08.0 + poppler_signing_data_get_image_path@Base 23.08.0 + poppler_signing_data_get_left_font_size@Base 23.08.0 + poppler_signing_data_get_location@Base 23.08.0 + poppler_signing_data_get_page@Base 23.08.0 + poppler_signing_data_get_password@Base 23.08.0 + poppler_signing_data_get_reason@Base 23.08.0 + poppler_signing_data_get_signature_rectangle@Base 23.08.0 + poppler_signing_data_get_signature_text@Base 23.08.0 + poppler_signing_data_get_signature_text_left@Base 23.08.0 + poppler_signing_data_get_type@Base 23.08.0 + poppler_signing_data_new@Base 23.08.0 + poppler_signing_data_set_background_color@Base 23.08.0 + poppler_signing_data_set_border_color@Base 23.08.0 + poppler_signing_data_set_border_width@Base 23.08.0 + poppler_signing_data_set_certificate_info@Base 23.08.0 + poppler_signing_data_set_destination_filename@Base 23.08.0 + poppler_signing_data_set_document_owner_password@Base 23.08.0 + poppler_signing_data_set_document_user_password@Base 23.08.0 + poppler_signing_data_set_field_partial_name@Base 23.08.0 + poppler_signing_data_set_font_color@Base 23.08.0 + poppler_signing_data_set_font_size@Base 23.08.0 + poppler_signing_data_set_image_path@Base 23.08.0 + poppler_signing_data_set_left_font_size@Base 23.08.0 + poppler_signing_data_set_location@Base 23.08.0 + poppler_signing_data_set_page@Base 23.08.0 + poppler_signing_data_set_password@Base 23.08.0 + poppler_signing_data_set_reason@Base 23.08.0 + poppler_signing_data_set_signature_rectangle@Base 23.08.0 + poppler_signing_data_set_signature_text@Base 23.08.0 + poppler_signing_data_set_signature_text_left@Base 23.08.0 + poppler_stretch_get_type@Base 25.01.0 poppler_structure_block_align_get_type@Base 0.26.0 poppler_structure_border_style_get_type@Base 0.26.0 poppler_structure_element_get_abbreviation@Base 0.26.0 @@ -460,6 +536,7 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_structure_text_align_get_type@Base 0.26.0 poppler_structure_text_decoration_get_type@Base 0.26.0 poppler_structure_writing_mode_get_type@Base 0.26.0 + poppler_style_get_type@Base 25.01.0 poppler_text_attributes_copy@Base 0.18.0 poppler_text_attributes_free@Base 0.18.0 poppler_text_attributes_get_type@Base 0.18.0 @@ -474,3 +551,4 @@ libpoppler-glib.so.8 libpoppler-glib8 #MINVER# poppler_text_span_is_fixed_width_font@Base 0.26.0 poppler_text_span_is_serif_font@Base 0.26.0 poppler_viewer_preferences_get_type@Base 0.18.0 + poppler_weight_get_type@Base 25.01.0 diff --git a/debian/libpoppler-qt5-1.symbols.in b/debian/libpoppler-qt5-1.symbols.in index 567db76..d319d45 100644 --- a/debian/libpoppler-qt5-1.symbols.in +++ b/debian/libpoppler-qt5-1.symbols.in @@ -55,6 +55,15 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler10LinkBrowseD0Ev@Base 0.24.5 _ZN7Poppler10LinkBrowseD1Ev@Base 0.24.5 _ZN7Poppler10LinkBrowseD2Ev@Base 0.24.5 + _ZN7Poppler11AsyncObject11qt_metacallEN11QMetaObject4CallEiPPv@Base 24.06.0 + _ZN7Poppler11AsyncObject11qt_metacastEPKc@Base 24.06.0 + _ZN7Poppler11AsyncObject16staticMetaObjectE@Base 24.06.0 + _ZN7Poppler11AsyncObject4doneEv@Base 24.06.0 + _ZN7Poppler11AsyncObjectC1Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectC2Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD0Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD1Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD2Ev@Base 24.06.0 _ZN7Poppler11LinkExecuteC1ERK6QRectFRK7QStringS6_@Base 0.24.5 _ZN7Poppler11LinkExecuteC2ERK6QRectFRK7QStringS6_@Base 0.24.5 _ZN7Poppler11LinkExecuteD0Ev@Base 0.24.5 @@ -86,6 +95,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler11PSConverter15setBottomMarginEi@Base 0.24.5 _ZN7Poppler11PSConverter16setStrictMarginsEb@Base 0.24.5 _ZN7Poppler11PSConverter17setForceRasterizeEb@Base 0.24.5 + _ZN7Poppler11PSConverter24setForceOverprintPreviewEb@Base 23.12.0 _ZN7Poppler11PSConverter24setPageConvertedCallbackEPFviPvES1_@Base 0.24.5 _ZN7Poppler11PSConverter7convertEv@Base 0.24.5 _ZN7Poppler11PSConverter7setHDPIEd@Base 0.24.5 @@ -183,11 +193,18 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler13InkAnnotationD0Ev@Base 0.24.5 _ZN7Poppler13InkAnnotationD1Ev@Base 0.24.5 _ZN7Poppler13InkAnnotationD2Ev@Base 0.24.5 + _ZN7Poppler13LinkRenditionC1ERK6QRectFOSt10unique_ptrI14MediaRenditionSt14default_deleteIS5_EEiRK7QString3Ref@Base 24.08.0 _ZN7Poppler13LinkRenditionC1ERK6QRectFP14MediaRenditioniRK7QStringRK3Ref@Base 0.24.5 + _ZN7Poppler13LinkRenditionC2ERK6QRectFOSt10unique_ptrI14MediaRenditionSt14default_deleteIS5_EEiRK7QString3Ref@Base 24.08.0 _ZN7Poppler13LinkRenditionC2ERK6QRectFP14MediaRenditioniRK7QStringRK3Ref@Base 0.24.5 _ZN7Poppler13LinkRenditionD0Ev@Base 0.24.5 _ZN7Poppler13LinkRenditionD1Ev@Base 0.24.5 _ZN7Poppler13LinkRenditionD2Ev@Base 0.24.5 + _ZN7Poppler13LinkResetFormC1EPNS_20LinkResetFormPrivateE@Base 24.08.0 + _ZN7Poppler13LinkResetFormC2EPNS_20LinkResetFormPrivateE@Base 24.08.0 + _ZN7Poppler13LinkResetFormD0Ev@Base 24.08.0 + _ZN7Poppler13LinkResetFormD1Ev@Base 24.08.0 + _ZN7Poppler13LinkResetFormD2Ev@Base 24.08.0 _ZN7Poppler13hasNSSSupportEv@Base 21.06.0 _ZN7Poppler14GeomAnnotation11setGeomTypeENS0_8GeomTypeE@Base 0.24.5 _ZN7Poppler14GeomAnnotation17setGeomInnerColorERK6QColor@Base 0.24.5 @@ -236,7 +253,14 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler14LinkJavaScriptD0Ev@Base 0.24.5 _ZN7Poppler14LinkJavaScriptD1Ev@Base 0.24.5 _ZN7Poppler14LinkJavaScriptD2Ev@Base 0.24.5 + _ZN7Poppler14LinkSubmitFormC1EPNS_21LinkSubmitFormPrivateE@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormC2EPNS_21LinkSubmitFormPrivateE@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD0Ev@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD1Ev@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD2Ev@Base 25.01.0 + _ZN7Poppler14MediaRenditionC1EOSt10unique_ptrI14MediaRenditionSt14default_deleteIS2_EE@Base 24.08.0 _ZN7Poppler14MediaRenditionC1EP14MediaRendition@Base 0.24.5 + _ZN7Poppler14MediaRenditionC2EOSt10unique_ptrI14MediaRenditionSt14default_deleteIS2_EE@Base 24.08.0 _ZN7Poppler14MediaRenditionC2EP14MediaRendition@Base 0.24.5 _ZN7Poppler14MediaRenditionD1Ev@Base 0.24.5 _ZN7Poppler14MediaRenditionD2Ev@Base 0.24.5 @@ -295,6 +319,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler15FormFieldButtonD2Ev@Base 0.24.5 _ZN7Poppler15FormFieldChoice13setEditChoiceERK7QString@Base 0.24.5 _ZN7Poppler15FormFieldChoice17setCurrentChoicesERK5QListIiE@Base 0.24.5 + _ZN7Poppler15FormFieldChoice23setAppearanceChoiceTextERK7QString@Base 24.08.0 _ZN7Poppler15FormFieldChoiceC1EPNS_12DocumentDataEP4PageP16FormWidgetChoice@Base 0.24.5 _ZN7Poppler15FormFieldChoiceC2EPNS_12DocumentDataEP4PageP16FormWidgetChoice@Base 0.24.5 _ZN7Poppler15FormFieldChoiceD0Ev@Base 0.24.5 @@ -325,8 +350,8 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler15OptContentModel16staticMetaObjectE@Base 0.24.5 _ZN7Poppler15OptContentModel7setDataERK11QModelIndexRK8QVarianti@Base 0.24.5 _ZN7Poppler15OptContentModel9applyLinkEPNS_12LinkOCGStateE@Base 0.57.0 - _ZN7Poppler15OptContentModelC1EP4OCGsP7QObject@Base 0.24.5 - _ZN7Poppler15OptContentModelC2EP4OCGsP7QObject@Base 0.24.5 + _ZN7Poppler15OptContentModelC1EPK4OCGsP7QObject@Base 25.03.0 + _ZN7Poppler15OptContentModelC2EPK4OCGsP7QObject@Base 25.03.0 _ZN7Poppler15OptContentModelD0Ev@Base 0.24.5 _ZN7Poppler15OptContentModelD1Ev@Base 0.24.5 _ZN7Poppler15OptContentModelD2Ev@Base 0.24.5 @@ -369,6 +394,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler16WidgetAnnotationD1Ev@Base 0.24.5 _ZN7Poppler16WidgetAnnotationD2Ev@Base 0.24.5 _ZN7Poppler16unicodeToQStringEPKji@Base 0.71.0 + _ZN7Poppler16unicodeToQStringERKSt6vectorIjSaIjEE@Base 24.02.0 _ZN7Poppler17FormFieldIconData7getDataERKNS_13FormFieldIconE@Base 0.81.0 _ZN7Poppler18FormFieldSignatureC1EPNS_12DocumentDataEP4PageP19FormWidgetSignature@Base 0.57.0 _ZN7Poppler18FormFieldSignatureC2EPNS_12DocumentDataEP4PageP19FormWidgetSignature@Base 0.57.0 @@ -459,6 +485,9 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler23SignatureValidationInfoD1Ev@Base 0.57.0 _ZN7Poppler23SignatureValidationInfoD2Ev@Base 0.57.0 _ZN7Poppler23SignatureValidationInfoaSERKS0_@Base 0.57.0 + _ZN7Poppler23activeCryptoSignBackendEv@Base 23.08.0 + _ZN7Poppler23arePgpSignaturesAllowedEv@Base 25.03.0 + _ZN7Poppler23setPgpSignaturesAllowedEb@Base 25.03.0 _ZN7Poppler24FileAttachmentAnnotation15setEmbeddedFileEPNS_12EmbeddedFileE@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotation15setFileIconNameERK7QString@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotationC1ERK8QDomNode@Base 0.24.5 @@ -471,6 +500,9 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler24FileAttachmentAnnotationD1Ev@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotationD2Ev@Base 0.24.5 _ZN7Poppler25QStringToUnicodeGooStringERK7QString@Base 0.71.0 + _ZN7Poppler26setActiveCryptoSignBackendENS_17CryptoSignBackendE@Base 23.08.0 + _ZN7Poppler27availableCryptoSignBackendsEv@Base 23.08.0 + _ZN7Poppler27hasCryptoSignBackendFeatureENS_17CryptoSignBackendENS_24CryptoSignBackendFeatureE@Base 23.08.0 _ZN7Poppler27isOverprintPreviewAvailableEv@Base 0.24.5 _ZN7Poppler31getAvailableSigningCertificatesEv@Base 21.06.0 _ZN7Poppler4LinkC1ERK6QRectF@Base 0.24.5 @@ -506,6 +538,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZN7Poppler8Document15linkDestinationERK7QString@Base 0.24.5 _ZN7Poppler8Document15setCreationDateERK9QDateTime@Base 0.46.0 _ZN7Poppler8Document16setRenderBackendENS0_13RenderBackendE@Base 0.24.5 + _ZN7Poppler8Document19applyResetFormsLinkERKNS_13LinkResetFormE@Base 24.08.0 _ZN7Poppler8Document19setModificationDateERK9QDateTime@Base 0.46.0 _ZN7Poppler8Document20optionalContentModelEv@Base 0.24.5 _ZN7Poppler8Document22setColorDisplayProfileEPv@Base 0.24.5 @@ -594,6 +627,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler10LinkAction8linkTypeEv@Base 0.24.5 _ZNK7Poppler10LinkBrowse3urlEv@Base 0.24.5 _ZNK7Poppler10LinkBrowse8linkTypeEv@Base 0.24.5 + _ZNK7Poppler11AsyncObject10metaObjectEv@Base 24.06.0 _ZNK7Poppler11LinkExecute10parametersEv@Base 0.24.5 _ZNK7Poppler11LinkExecute8fileNameEv@Base 0.24.5 _ZNK7Poppler11LinkExecute8linkTypeEv@Base 0.24.5 @@ -668,6 +702,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler13LinkRendition6scriptEv@Base 0.24.5 _ZNK7Poppler13LinkRendition8linkTypeEv@Base 0.24.5 _ZNK7Poppler13LinkRendition9renditionEv@Base 0.24.5 + _ZNK7Poppler13LinkResetForm8linkTypeEv@Base 24.08.0 _ZNK7Poppler14GeomAnnotation14geomInnerColorEv@Base 0.24.5 _ZNK7Poppler14GeomAnnotation5storeER8QDomNodeR12QDomDocument@Base 0.24.5 _ZNK7Poppler14GeomAnnotation7subTypeEv@Base 0.24.5 @@ -691,6 +726,10 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler14LinkAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler14LinkJavaScript6scriptEv@Base 0.24.5 _ZNK7Poppler14LinkJavaScript8linkTypeEv@Base 0.24.5 + _ZNK7Poppler14LinkSubmitForm11getFieldIdsEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm6getUrlEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm8getFlagsEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm8linkTypeEv@Base 25.01.0 _ZNK7Poppler14MediaRendition10isEmbeddedEv@Base 0.24.5 _ZNK7Poppler14MediaRendition11contentTypeEv@Base 0.24.5 _ZNK7Poppler14MediaRendition11repeatCountEv@Base 0.24.5 @@ -722,6 +761,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler15CaretAnnotation5storeER8QDomNodeR12QDomDocument@Base 0.24.5 _ZNK7Poppler15CaretAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler15CertificateInfo10issuerInfoENS0_13EntityInfoKeyE@Base 0.81.0 + _ZNK7Poppler15CertificateInfo11keyLocationEv@Base 23.12.0 _ZNK7Poppler15CertificateInfo11subjectInfoENS0_13EntityInfoKeyE@Base 0.81.0 _ZNK7Poppler15CertificateInfo11validityEndEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo12isSelfSignedEv@Base 0.81.0 @@ -730,6 +770,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler15CertificateInfo13publicKeyTypeEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo13validityStartEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo15certificateDataEv@Base 0.81.0 + _ZNK7Poppler15CertificateInfo15certificateTypeEv@Base 25.03.0 _ZNK7Poppler15CertificateInfo17publicKeyStrengthEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo18keyUsageExtensionsEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo6isNullEv@Base 0.81.0 @@ -792,6 +833,8 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler16WidgetAnnotation5storeER8QDomNodeR12QDomDocument@Base 0.24.5 _ZNK7Poppler16WidgetAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler18FormFieldSignature13signatureTypeEv@Base 0.59.0 + _ZNK7Poppler18FormFieldSignature13validateAsyncENS0_15ValidateOptionsERK9QDateTime@Base 24.06.0 + _ZNK7Poppler18FormFieldSignature14validateResultEv@Base 24.06.0 _ZNK7Poppler18FormFieldSignature4signERK7QStringRKNS_12PDFConverter16NewSignatureDataE@Base 22.02.0 _ZNK7Poppler18FormFieldSignature4typeEv@Base 0.57.0 _ZNK7Poppler18FormFieldSignature8validateENS0_15ValidateOptionsE@Base 0.57.0 @@ -887,6 +930,7 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler8Document13textDirectionEv@Base 0.26.0 _ZNK7Poppler8Document15colorRgbProfileEv@Base 0.24.5 _ZNK7Poppler8Document15newFontIteratorEi@Base 0.24.5 + _ZNK7Poppler8Document16additionalActionENS0_29DocumentAdditionalActionsTypeE@Base 24.08.0 _ZNK7Poppler8Document16hasEmbeddedFilesEv@Base 0.24.5 _ZNK7Poppler8Document16modificationDateEv@Base 0.46.0 _ZNK7Poppler8Document16okToPrintHighResEv@Base 0.24.5 @@ -955,24 +999,11 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZNK7Poppler9LinkSound6repeatEv@Base 0.24.5 _ZNK7Poppler9LinkSound6volumeEv@Base 0.24.5 _ZNK7Poppler9LinkSound8linkTypeEv@Base 0.24.5 - _ZNSt15_Sp_counted_ptrIDnLN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 0.90.0 - _ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 0.90.0 - (optional=gccinternal)_ZNSt15_Sp_counted_ptrIDnLN9__gnu_cxx12_Lock_policyE2EE10_M_disposeEv@Base 0.90.0 20.09.0 - (optional=gccinternal)_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE10_M_destroyEv@Base 0.90.0 20.09.0 - (optional=gccinternal)_ZNSt5dequeIP8QPainterSaIS1_EE16_M_push_back_auxIJRKS1_EEEvDpOT_@Base 20.09.0 - (optional=gccinternal)_ZNSt5dequeIP8QPainterSaIS1_EE17_M_reallocate_mapEjb@Base 22.02.0 - (optional=gccinternal)_ZNSt5dequeIP8QPainterSaIS1_EE17_M_reallocate_mapEmb@Base 0.71.0 - (optional=gccinternal)_ZNSt5dequeIPiSaIS0_EE16_M_push_back_auxIJRKS0_EEEvDpOT_@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorISt10unique_ptrI8QPictureSt14default_deleteIS1_EESaIS4_EE17_M_default_appendEj@Base 22.02.0 - (optional=gccinternal)_ZNSt6vectorISt10unique_ptrI8QPictureSt14default_deleteIS1_EESaIS4_EE17_M_default_appendEm@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorIiSaIiEE17_M_default_appendEj@Base 22.02.0 - (optional=gccinternal)_ZNSt6vectorIiSaIiEE17_M_default_appendEm@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 0.57.0 - (optional=gccinternal)_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag@Base 0.71.0 _ZTI11QSharedData@Base 0.24.5 _ZTIN7Poppler10AnnotationE@Base 0.24.5 _ZTIN7Poppler10LinkActionE@Base 0.24.5 _ZTIN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTIN7Poppler11AsyncObjectE@Base 24.06.0 _ZTIN7Poppler11LinkExecuteE@Base 0.24.5 _ZTIN7Poppler11PSConverterE@Base 0.24.5 _ZTIN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -981,10 +1012,12 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTIN7Poppler13FormFieldTextE@Base 0.24.5 _ZTIN7Poppler13InkAnnotationE@Base 0.24.5 _ZTIN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTIN7Poppler13LinkResetFormE@Base 24.08.0 _ZTIN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTIN7Poppler14LineAnnotationE@Base 0.24.5 _ZTIN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTIN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTIN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTIN7Poppler14TextAnnotationE@Base 0.24.5 _ZTIN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTIN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -1005,10 +1038,15 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTIN7Poppler9FormFieldE@Base 0.24.5 _ZTIN7Poppler9LinkMovieE@Base 0.24.5 _ZTIN7Poppler9LinkSoundE@Base 0.24.5 + (arch=armel riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + (arch=armel riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 _ZTS11QSharedData@Base 0.24.5 _ZTSN7Poppler10AnnotationE@Base 0.24.5 _ZTSN7Poppler10LinkActionE@Base 0.24.5 _ZTSN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTSN7Poppler11AsyncObjectE@Base 24.06.0 _ZTSN7Poppler11LinkExecuteE@Base 0.24.5 _ZTSN7Poppler11PSConverterE@Base 0.24.5 _ZTSN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -1017,10 +1055,12 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTSN7Poppler13FormFieldTextE@Base 0.24.5 _ZTSN7Poppler13InkAnnotationE@Base 0.24.5 _ZTSN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTSN7Poppler13LinkResetFormE@Base 24.08.0 _ZTSN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTSN7Poppler14LineAnnotationE@Base 0.24.5 _ZTSN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTSN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTSN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTSN7Poppler14TextAnnotationE@Base 0.24.5 _ZTSN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTSN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -1041,9 +1081,17 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTSN7Poppler9FormFieldE@Base 0.24.5 _ZTSN7Poppler9LinkMovieE@Base 0.24.5 _ZTSN7Poppler9LinkSoundE@Base 0.24.5 + (arch=armel riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + (arch=armel riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + _ZTSSt19_Sp_make_shared_tag@Base 24.06.0 + (arch=armel riscv64)_ZTSSt23_Sp_counted_ptr_inplaceIN7Poppler11AsyncObjectESaIvELN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt23_Sp_counted_ptr_inplaceIN7Poppler11AsyncObjectESaIvELN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 _ZTVN7Poppler10AnnotationE@Base 0.24.5 _ZTVN7Poppler10LinkActionE@Base 0.24.5 _ZTVN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTVN7Poppler11AsyncObjectE@Base 24.06.0 _ZTVN7Poppler11LinkExecuteE@Base 0.24.5 _ZTVN7Poppler11PSConverterE@Base 0.24.5 _ZTVN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -1052,10 +1100,12 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTVN7Poppler13FormFieldTextE@Base 0.24.5 _ZTVN7Poppler13InkAnnotationE@Base 0.24.5 _ZTVN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTVN7Poppler13LinkResetFormE@Base 24.08.0 _ZTVN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTVN7Poppler14LineAnnotationE@Base 0.24.5 _ZTVN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTVN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTVN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTVN7Poppler14TextAnnotationE@Base 0.24.5 _ZTVN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTVN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -1076,3 +1126,4 @@ libpoppler-qt5.so.1 libpoppler-qt5-1 #MINVER# _ZTVN7Poppler9FormFieldE@Base 0.24.5 _ZTVN7Poppler9LinkMovieE@Base 0.24.5 _ZTVN7Poppler9LinkSoundE@Base 0.24.5 + _ZZNSt19_Sp_make_shared_tag5_S_tiEvE5__tag@Base 24.06.0 diff --git a/debian/libpoppler-qt6-3.symbols.in b/debian/libpoppler-qt6-3.symbols.in index 03b90d4..4fef3e3 100644 --- a/debian/libpoppler-qt6-3.symbols.in +++ b/debian/libpoppler-qt6-3.symbols.in @@ -53,6 +53,15 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler10LinkBrowseD0Ev@Base 0.24.5 _ZN7Poppler10LinkBrowseD1Ev@Base 0.24.5 _ZN7Poppler10LinkBrowseD2Ev@Base 0.24.5 + _ZN7Poppler11AsyncObject11qt_metacallEN11QMetaObject4CallEiPPv@Base 24.06.0 + _ZN7Poppler11AsyncObject11qt_metacastEPKc@Base 24.06.0 + _ZN7Poppler11AsyncObject16staticMetaObjectE@Base 24.06.0 + _ZN7Poppler11AsyncObject4doneEv@Base 24.06.0 + _ZN7Poppler11AsyncObjectC1Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectC2Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD0Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD1Ev@Base 24.06.0 + _ZN7Poppler11AsyncObjectD2Ev@Base 24.06.0 _ZN7Poppler11LinkExecuteC1ERK6QRectFRK7QStringS6_@Base 0.24.5 _ZN7Poppler11LinkExecuteC2ERK6QRectFRK7QStringS6_@Base 0.24.5 _ZN7Poppler11LinkExecuteD0Ev@Base 0.24.5 @@ -84,6 +93,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler11PSConverter15setBottomMarginEi@Base 0.24.5 _ZN7Poppler11PSConverter16setStrictMarginsEb@Base 0.24.5 _ZN7Poppler11PSConverter17setForceRasterizeEb@Base 0.24.5 + _ZN7Poppler11PSConverter24setForceOverprintPreviewEb@Base 23.12.0 _ZN7Poppler11PSConverter24setPageConvertedCallbackEPFviPvES1_@Base 0.24.5 _ZN7Poppler11PSConverter7convertEv@Base 0.24.5 _ZN7Poppler11PSConverter7setHDPIEd@Base 0.24.5 @@ -178,11 +188,18 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler13InkAnnotationD0Ev@Base 0.24.5 _ZN7Poppler13InkAnnotationD1Ev@Base 0.24.5 _ZN7Poppler13InkAnnotationD2Ev@Base 0.24.5 + _ZN7Poppler13LinkRenditionC1ERK6QRectFOSt10unique_ptrI14MediaRenditionSt14default_deleteIS5_EEiRK7QString3Ref@Base 24.08.0 _ZN7Poppler13LinkRenditionC1ERK6QRectFP14MediaRenditioniRK7QString3Ref@Base 22.11.0 + _ZN7Poppler13LinkRenditionC2ERK6QRectFOSt10unique_ptrI14MediaRenditionSt14default_deleteIS5_EEiRK7QString3Ref@Base 24.08.0 _ZN7Poppler13LinkRenditionC2ERK6QRectFP14MediaRenditioniRK7QString3Ref@Base 22.11.0 _ZN7Poppler13LinkRenditionD0Ev@Base 0.24.5 _ZN7Poppler13LinkRenditionD1Ev@Base 0.24.5 _ZN7Poppler13LinkRenditionD2Ev@Base 0.24.5 + _ZN7Poppler13LinkResetFormC1EPNS_20LinkResetFormPrivateE@Base 24.08.0 + _ZN7Poppler13LinkResetFormC2EPNS_20LinkResetFormPrivateE@Base 24.08.0 + _ZN7Poppler13LinkResetFormD0Ev@Base 24.08.0 + _ZN7Poppler13LinkResetFormD1Ev@Base 24.08.0 + _ZN7Poppler13LinkResetFormD2Ev@Base 24.08.0 _ZN7Poppler13hasNSSSupportEv@Base 21.06.0 _ZN7Poppler14GeomAnnotation11setGeomTypeENS0_8GeomTypeE@Base 0.24.5 _ZN7Poppler14GeomAnnotation17setGeomInnerColorERK6QColor@Base 0.24.5 @@ -225,7 +242,14 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler14LinkJavaScriptD0Ev@Base 0.24.5 _ZN7Poppler14LinkJavaScriptD1Ev@Base 0.24.5 _ZN7Poppler14LinkJavaScriptD2Ev@Base 0.24.5 + _ZN7Poppler14LinkSubmitFormC1EPNS_21LinkSubmitFormPrivateE@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormC2EPNS_21LinkSubmitFormPrivateE@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD0Ev@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD1Ev@Base 25.01.0 + _ZN7Poppler14LinkSubmitFormD2Ev@Base 25.01.0 + _ZN7Poppler14MediaRenditionC1EOSt10unique_ptrI14MediaRenditionSt14default_deleteIS2_EE@Base 24.08.0 _ZN7Poppler14MediaRenditionC1EP14MediaRendition@Base 0.24.5 + _ZN7Poppler14MediaRenditionC2EOSt10unique_ptrI14MediaRenditionSt14default_deleteIS2_EE@Base 24.08.0 _ZN7Poppler14MediaRenditionC2EP14MediaRendition@Base 0.24.5 _ZN7Poppler14MediaRenditionD1Ev@Base 0.24.5 _ZN7Poppler14MediaRenditionD2Ev@Base 0.24.5 @@ -277,6 +301,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler15FormFieldButtonD2Ev@Base 0.24.5 _ZN7Poppler15FormFieldChoice13setEditChoiceERK7QString@Base 0.24.5 _ZN7Poppler15FormFieldChoice17setCurrentChoicesERK5QListIiE@Base 0.24.5 + _ZN7Poppler15FormFieldChoice23setAppearanceChoiceTextERK7QString@Base 24.08.0 _ZN7Poppler15FormFieldChoiceC1EPNS_12DocumentDataEP4PageP16FormWidgetChoice@Base 0.24.5 _ZN7Poppler15FormFieldChoiceC2EPNS_12DocumentDataEP4PageP16FormWidgetChoice@Base 0.24.5 _ZN7Poppler15FormFieldChoiceD0Ev@Base 0.24.5 @@ -305,8 +330,8 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler15OptContentModel16staticMetaObjectE@Base 0.24.5 _ZN7Poppler15OptContentModel7setDataERK11QModelIndexRK8QVarianti@Base 0.24.5 _ZN7Poppler15OptContentModel9applyLinkEPNS_12LinkOCGStateE@Base 0.57.0 - _ZN7Poppler15OptContentModelC1EP4OCGsP7QObject@Base 0.24.5 - _ZN7Poppler15OptContentModelC2EP4OCGsP7QObject@Base 0.24.5 + _ZN7Poppler15OptContentModelC1EPK4OCGsP7QObject@Base 25.03.0 + _ZN7Poppler15OptContentModelC2EPK4OCGsP7QObject@Base 25.03.0 _ZN7Poppler15OptContentModelD0Ev@Base 0.24.5 _ZN7Poppler15OptContentModelD1Ev@Base 0.24.5 _ZN7Poppler15OptContentModelD2Ev@Base 0.24.5 @@ -345,6 +370,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler16WidgetAnnotationD1Ev@Base 0.24.5 _ZN7Poppler16WidgetAnnotationD2Ev@Base 0.24.5 _ZN7Poppler16unicodeToQStringEPKji@Base 0.71.0 + _ZN7Poppler16unicodeToQStringERKSt6vectorIjSaIjEE@Base 24.02.0 _ZN7Poppler17FormFieldIconData7getDataERKNS_13FormFieldIconE@Base 0.81.0 _ZN7Poppler18FormFieldSignatureC1EPNS_12DocumentDataEP4PageP19FormWidgetSignature@Base 0.57.0 _ZN7Poppler18FormFieldSignatureC2EPNS_12DocumentDataEP4PageP19FormWidgetSignature@Base 0.57.0 @@ -416,6 +442,24 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler19RichMediaAnnotationD0Ev@Base 0.38.0 _ZN7Poppler19RichMediaAnnotationD1Ev@Base 0.38.0 _ZN7Poppler19RichMediaAnnotationD2Ev@Base 0.38.0 + _ZN7Poppler19SignatureAnnotation11setFontSizeEd@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation11setLeftTextERK7QString@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation12setFontColorERK6QColor@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation12setImagePathERK7QString@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation14setBorderColorERK6QColor@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation14setBorderWidthEd@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation15setLeftFontSizeEd@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation18setBackgroundColorERK6QColor@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation19setFieldPartialNameERK7QString@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation4signERK7QStringRKNS_12PDFConverter16NewSignatureDataE@Base 25.01.0 + _ZN7Poppler19SignatureAnnotation7setTextERK7QString@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationC1ERNS_26SignatureAnnotationPrivateE@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationC1Ev@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationC2ERNS_26SignatureAnnotationPrivateE@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationC2Ev@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationD0Ev@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationD1Ev@Base 25.01.0 + _ZN7Poppler19SignatureAnnotationD2Ev@Base 25.01.0 _ZN7Poppler19UnicodeParsedStringEPK9GooString@Base 0.71.0 _ZN7Poppler19UnicodeParsedStringERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE@Base 0.90.0 _ZN7Poppler20AnnotationAppearanceC1EPNS_27AnnotationAppearancePrivateE@Base 21.11.0 @@ -431,6 +475,9 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler23SignatureValidationInfoD1Ev@Base 0.57.0 _ZN7Poppler23SignatureValidationInfoD2Ev@Base 0.57.0 _ZN7Poppler23SignatureValidationInfoaSERKS0_@Base 0.57.0 + _ZN7Poppler23activeCryptoSignBackendEv@Base 23.08.0 + _ZN7Poppler23arePgpSignaturesAllowedEv@Base 25.03.0 + _ZN7Poppler23setPgpSignaturesAllowedEb@Base 25.03.0 _ZN7Poppler24FileAttachmentAnnotation15setEmbeddedFileEPNS_12EmbeddedFileE@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotation15setFileIconNameERK7QString@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotationC1ERNS_31FileAttachmentAnnotationPrivateE@Base 0.24.5 @@ -441,6 +488,9 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler24FileAttachmentAnnotationD1Ev@Base 0.24.5 _ZN7Poppler24FileAttachmentAnnotationD2Ev@Base 0.24.5 _ZN7Poppler25QStringToUnicodeGooStringERK7QString@Base 0.71.0 + _ZN7Poppler26setActiveCryptoSignBackendENS_17CryptoSignBackendE@Base 23.08.0 + _ZN7Poppler27availableCryptoSignBackendsEv@Base 23.08.0 + _ZN7Poppler27hasCryptoSignBackendFeatureENS_17CryptoSignBackendENS_24CryptoSignBackendFeatureE@Base 23.08.0 _ZN7Poppler27isOverprintPreviewAvailableEv@Base 0.24.5 _ZN7Poppler31getAvailableSigningCertificatesEv@Base 21.06.0 _ZN7Poppler4LinkC1ERK6QRectF@Base 0.24.5 @@ -476,6 +526,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZN7Poppler8Document15linkDestinationERK7QString@Base 0.24.5 _ZN7Poppler8Document15setCreationDateERK9QDateTime@Base 0.46.0 _ZN7Poppler8Document16setRenderBackendENS0_13RenderBackendE@Base 0.24.5 + _ZN7Poppler8Document19applyResetFormsLinkERKNS_13LinkResetFormE@Base 24.08.0 _ZN7Poppler8Document19setModificationDateERK9QDateTime@Base 0.46.0 _ZN7Poppler8Document20optionalContentModelEv@Base 0.24.5 _ZN7Poppler8Document22setColorDisplayProfileEPv@Base 0.24.5 @@ -563,6 +614,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler10LinkAction8linkTypeEv@Base 0.24.5 _ZNK7Poppler10LinkBrowse3urlEv@Base 0.24.5 _ZNK7Poppler10LinkBrowse8linkTypeEv@Base 0.24.5 + _ZNK7Poppler11AsyncObject10metaObjectEv@Base 24.06.0 _ZNK7Poppler11LinkExecute10parametersEv@Base 0.24.5 _ZNK7Poppler11LinkExecute8fileNameEv@Base 0.24.5 _ZNK7Poppler11LinkExecute8linkTypeEv@Base 0.24.5 @@ -636,6 +688,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler13LinkRendition6scriptEv@Base 0.24.5 _ZNK7Poppler13LinkRendition8linkTypeEv@Base 0.24.5 _ZNK7Poppler13LinkRendition9renditionEv@Base 0.24.5 + _ZNK7Poppler13LinkResetForm8linkTypeEv@Base 24.08.0 _ZNK7Poppler14GeomAnnotation14geomInnerColorEv@Base 0.24.5 _ZNK7Poppler14GeomAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler14GeomAnnotation8geomTypeEv@Base 0.24.5 @@ -656,6 +709,10 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler14LinkAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler14LinkJavaScript6scriptEv@Base 0.24.5 _ZNK7Poppler14LinkJavaScript8linkTypeEv@Base 0.24.5 + _ZNK7Poppler14LinkSubmitForm11getFieldIdsEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm6getUrlEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm8getFlagsEv@Base 25.01.0 + _ZNK7Poppler14LinkSubmitForm8linkTypeEv@Base 25.01.0 _ZNK7Poppler14MediaRendition10isEmbeddedEv@Base 0.24.5 _ZNK7Poppler14MediaRendition11contentTypeEv@Base 0.24.5 _ZNK7Poppler14MediaRendition11repeatCountEv@Base 0.24.5 @@ -684,6 +741,8 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler15CaretAnnotation11caretSymbolEv@Base 0.24.5 _ZNK7Poppler15CaretAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler15CertificateInfo10issuerInfoENS0_13EntityInfoKeyE@Base 0.81.0 + _ZNK7Poppler15CertificateInfo11isQualifiedEv@Base 25.01.0 + _ZNK7Poppler15CertificateInfo11keyLocationEv@Base 23.12.0 _ZNK7Poppler15CertificateInfo11subjectInfoENS0_13EntityInfoKeyE@Base 0.81.0 _ZNK7Poppler15CertificateInfo11validityEndEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo12isSelfSignedEv@Base 0.81.0 @@ -692,6 +751,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler15CertificateInfo13publicKeyTypeEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo13validityStartEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo15certificateDataEv@Base 0.81.0 + _ZNK7Poppler15CertificateInfo15certificateTypeEv@Base 25.03.0 _ZNK7Poppler15CertificateInfo17publicKeyStrengthEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo18keyUsageExtensionsEv@Base 0.81.0 _ZNK7Poppler15CertificateInfo6isNullEv@Base 0.81.0 @@ -749,6 +809,8 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler16WidgetAnnotation16additionalActionENS_10Annotation20AdditionalActionTypeE@Base 0.24.5 _ZNK7Poppler16WidgetAnnotation7subTypeEv@Base 0.24.5 _ZNK7Poppler18FormFieldSignature13signatureTypeEv@Base 0.59.0 + _ZNK7Poppler18FormFieldSignature13validateAsyncENS0_15ValidateOptionsERK9QDateTime@Base 24.06.0 + _ZNK7Poppler18FormFieldSignature14validateResultEv@Base 24.06.0 _ZNK7Poppler18FormFieldSignature4signERK7QStringRKNS_12PDFConverter16NewSignatureDataE@Base 22.02.0 _ZNK7Poppler18FormFieldSignature4typeEv@Base 0.57.0 _ZNK7Poppler18FormFieldSignature8validateENS0_15ValidateOptionsE@Base 0.57.0 @@ -773,6 +835,15 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler19RichMediaAnnotation8Settings10activationEv@Base 0.38.0 _ZNK7Poppler19RichMediaAnnotation8Settings12deactivationEv@Base 0.38.0 _ZNK7Poppler19RichMediaAnnotation8settingsEv@Base 0.38.0 + _ZNK7Poppler19SignatureAnnotation11borderColorEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation11borderWidthEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation12leftFontSizeEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation15backgroundColorEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation16fieldPartialNameEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation7subTypeEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation8fontSizeEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation9fontColorEv@Base 25.01.0 + _ZNK7Poppler19SignatureAnnotation9imagePathEv@Base 25.01.0 _ZNK7Poppler23SignatureValidationInfo10signerNameEv@Base 0.57.0 _ZNK7Poppler23SignatureValidationInfo11signingTimeEv@Base 0.57.0 _ZNK7Poppler23SignatureValidationInfo13hashAlgorithmEv@Base 0.59.0 @@ -838,6 +909,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler8Document13textDirectionEv@Base 0.26.0 _ZNK7Poppler8Document15colorRgbProfileEv@Base 0.24.5 _ZNK7Poppler8Document15newFontIteratorEi@Base 0.24.5 + _ZNK7Poppler8Document16additionalActionENS0_29DocumentAdditionalActionsTypeE@Base 24.08.0 _ZNK7Poppler8Document16hasEmbeddedFilesEv@Base 0.24.5 _ZNK7Poppler8Document16modificationDateEv@Base 0.46.0 _ZNK7Poppler8Document16okToPrintHighResEv@Base 0.24.5 @@ -906,15 +978,10 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZNK7Poppler9LinkSound6volumeEv@Base 0.24.5 _ZNK7Poppler9LinkSound8linkTypeEv@Base 0.24.5 (optional=gccinternal)_ZSt19piecewise_construct@Base 22.12.0 - (optional=gccinternal)_ZNSt5dequeIP8QPainterSaIS1_EE17_M_reallocate_mapEmb@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorISt10unique_ptrI8QPictureSt14default_deleteIS1_EESaIS4_EE17_M_default_appendEm@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorISt10unique_ptrIN7Poppler7TextBoxESt14default_deleteIS2_EESaIS5_EE17_M_realloc_insertIJS5_EEEvN9__gnu_cxx17__normal_iteratorIPS5_S7_EEDpOT_@Base 22.11.0 - (optional=gccinternal)_ZNSt6vectorIiSaIiEE17_M_default_appendEm@Base 0.71.0 - (optional=gccinternal)_ZNSt6vectorISt10unique_ptrIN7Poppler10AnnotationESt14default_deleteIS2_EESaIS5_EE17_M_realloc_insertIJS5_EEEvN9__gnu_cxx17__normal_iteratorIPS5_S7_EEDpOT_@Base 22.11.0 - (optional=gccinternal)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 0.57.0 _ZTIN7Poppler10AnnotationE@Base 0.24.5 _ZTIN7Poppler10LinkActionE@Base 0.24.5 _ZTIN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTIN7Poppler11AsyncObjectE@Base 24.06.0 _ZTIN7Poppler11LinkExecuteE@Base 0.24.5 _ZTIN7Poppler11PSConverterE@Base 0.24.5 _ZTIN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -923,10 +990,12 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTIN7Poppler13FormFieldTextE@Base 0.24.5 _ZTIN7Poppler13InkAnnotationE@Base 0.24.5 _ZTIN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTIN7Poppler13LinkResetFormE@Base 24.08.0 _ZTIN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTIN7Poppler14LineAnnotationE@Base 0.24.5 _ZTIN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTIN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTIN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTIN7Poppler14TextAnnotationE@Base 0.24.5 _ZTIN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTIN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -940,6 +1009,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTIN7Poppler18FormFieldSignatureE@Base 0.57.0 _ZTIN7Poppler19HighlightAnnotationE@Base 0.24.5 _ZTIN7Poppler19RichMediaAnnotationE@Base 0.38.0 + _ZTIN7Poppler19SignatureAnnotationE@Base 25.01.0 _ZTIN7Poppler24FileAttachmentAnnotationE@Base 0.24.5 _ZTIN7Poppler4LinkE@Base 0.24.5 _ZTIN7Poppler8LinkGotoE@Base 0.24.5 @@ -947,9 +1017,14 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTIN7Poppler9FormFieldE@Base 0.24.5 _ZTIN7Poppler9LinkMovieE@Base 0.24.5 _ZTIN7Poppler9LinkSoundE@Base 0.24.5 + (arch=armel riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTISt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + (arch=armel riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTISt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 _ZTSN7Poppler10AnnotationE@Base 0.24.5 _ZTSN7Poppler10LinkActionE@Base 0.24.5 _ZTSN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTSN7Poppler11AsyncObjectE@Base 24.06.0 _ZTSN7Poppler11LinkExecuteE@Base 0.24.5 _ZTSN7Poppler11PSConverterE@Base 0.24.5 _ZTSN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -958,10 +1033,12 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTSN7Poppler13FormFieldTextE@Base 0.24.5 _ZTSN7Poppler13InkAnnotationE@Base 0.24.5 _ZTSN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTSN7Poppler13LinkResetFormE@Base 24.08.0 _ZTSN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTSN7Poppler14LineAnnotationE@Base 0.24.5 _ZTSN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTSN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTSN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTSN7Poppler14TextAnnotationE@Base 0.24.5 _ZTSN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTSN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -975,6 +1052,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTSN7Poppler18FormFieldSignatureE@Base 0.57.0 _ZTSN7Poppler19HighlightAnnotationE@Base 0.24.5 _ZTSN7Poppler19RichMediaAnnotationE@Base 0.38.0 + _ZTSN7Poppler19SignatureAnnotationE@Base 25.01.0 _ZTSN7Poppler24FileAttachmentAnnotationE@Base 0.24.5 _ZTSN7Poppler4LinkE@Base 0.24.5 _ZTSN7Poppler8LinkGotoE@Base 0.24.5 @@ -982,9 +1060,17 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTSN7Poppler9FormFieldE@Base 0.24.5 _ZTSN7Poppler9LinkMovieE@Base 0.24.5 _ZTSN7Poppler9LinkSoundE@Base 0.24.5 + (arch=armel riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt11_Mutex_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + (arch=armel riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 + _ZTSSt19_Sp_make_shared_tag@Base 24.06.0 + (arch=armel riscv64)_ZTSSt23_Sp_counted_ptr_inplaceIN7Poppler11AsyncObjectESaIvELN9__gnu_cxx12_Lock_policyE1EE@Base 24.06.0 + (arch=!armel !riscv64)_ZTSSt23_Sp_counted_ptr_inplaceIN7Poppler11AsyncObjectESaIvELN9__gnu_cxx12_Lock_policyE2EE@Base 24.06.0 _ZTVN7Poppler10AnnotationE@Base 0.24.5 _ZTVN7Poppler10LinkActionE@Base 0.24.5 _ZTVN7Poppler10LinkBrowseE@Base 0.24.5 + _ZTVN7Poppler11AsyncObjectE@Base 24.06.0 _ZTVN7Poppler11LinkExecuteE@Base 0.24.5 _ZTVN7Poppler11PSConverterE@Base 0.24.5 _ZTVN7Poppler12LinkOCGStateE@Base 0.57.0 @@ -993,10 +1079,12 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTVN7Poppler13FormFieldTextE@Base 0.24.5 _ZTVN7Poppler13InkAnnotationE@Base 0.24.5 _ZTVN7Poppler13LinkRenditionE@Base 0.24.5 + _ZTVN7Poppler13LinkResetFormE@Base 24.08.0 _ZTVN7Poppler14GeomAnnotationE@Base 0.24.5 _ZTVN7Poppler14LineAnnotationE@Base 0.24.5 _ZTVN7Poppler14LinkAnnotationE@Base 0.24.5 _ZTVN7Poppler14LinkJavaScriptE@Base 0.24.5 + _ZTVN7Poppler14LinkSubmitFormE@Base 25.01.0 _ZTVN7Poppler14TextAnnotationE@Base 0.24.5 _ZTVN7Poppler15CaretAnnotationE@Base 0.24.5 _ZTVN7Poppler15FormFieldButtonE@Base 0.24.5 @@ -1010,6 +1098,7 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTVN7Poppler18FormFieldSignatureE@Base 0.57.0 _ZTVN7Poppler19HighlightAnnotationE@Base 0.24.5 _ZTVN7Poppler19RichMediaAnnotationE@Base 0.38.0 + _ZTVN7Poppler19SignatureAnnotationE@Base 25.01.0 _ZTVN7Poppler24FileAttachmentAnnotationE@Base 0.24.5 _ZTVN7Poppler4LinkE@Base 0.24.5 _ZTVN7Poppler8LinkGotoE@Base 0.24.5 @@ -1017,3 +1106,4 @@ libpoppler-qt6.so.3 libpoppler-qt6-3 #MINVER# _ZTVN7Poppler9FormFieldE@Base 0.24.5 _ZTVN7Poppler9LinkMovieE@Base 0.24.5 _ZTVN7Poppler9LinkSoundE@Base 0.24.5 + _ZZNSt19_Sp_make_shared_tag5_S_tiEvE5__tag@Base 24.06.0 diff --git a/debian/libpoppler126.docs b/debian/libpoppler147.docs similarity index 100% rename from debian/libpoppler126.docs rename to debian/libpoppler147.docs index 3b88b28..0d71641 100644 --- a/debian/libpoppler126.docs +++ b/debian/libpoppler147.docs @@ -1,4 +1,4 @@ AUTHORS NEWS -README.md README-XPDF +README.md diff --git a/debian/libpoppler126.install b/debian/libpoppler147.install similarity index 100% rename from debian/libpoppler126.install rename to debian/libpoppler147.install diff --git a/debian/patches/0001-OutlineItem-open-Fix-crash-on-malformed-files.patch b/debian/patches/0001-OutlineItem-open-Fix-crash-on-malformed-files.patch deleted file mode 100644 index 4063062..0000000 --- a/debian/patches/0001-OutlineItem-open-Fix-crash-on-malformed-files.patch +++ /dev/null @@ -1,41 +0,0 @@ -From db4e10de064c6b8bddbcbcb042116c6d70ed1e35 Mon Sep 17 00:00:00 2001 -From: Albert Astals Cid -Date: Wed, 17 May 2023 22:42:05 +0200 -Subject: OutlineItem::open: Fix crash on malformed files - -Fixes #1399 ---- - poppler/Outline.cc | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/poppler/Outline.cc b/poppler/Outline.cc -index cbb6cb49..4c68be99 100644 ---- a/poppler/Outline.cc -+++ b/poppler/Outline.cc -@@ -14,7 +14,7 @@ - // under GPL version 2 or later - // - // Copyright (C) 2005 Marco Pesenti Gritti --// Copyright (C) 2008, 2016-2019, 2021 Albert Astals Cid -+// Copyright (C) 2008, 2016-2019, 2021, 2023 Albert Astals Cid - // Copyright (C) 2009 Nick Jones - // Copyright (C) 2016 Jason Crain - // Copyright (C) 2017 Adrian Johnson -@@ -483,8 +483,12 @@ void OutlineItem::open() - { - if (!kids) { - Object itemDict = xref->fetch(ref); -- const Object &firstRef = itemDict.dictLookupNF("First"); -- kids = readItemList(this, &firstRef, xref, doc); -+ if (itemDict.isDict()) { -+ const Object &firstRef = itemDict.dictLookupNF("First"); -+ kids = readItemList(this, &firstRef, xref, doc); -+ } else { -+ kids = new std::vector(); -+ } - } - } - --- -2.30.2 - diff --git a/debian/patches/0002-JBIG2Bitmap-combine-Fix-crash-on-malformed-files.patch b/debian/patches/0002-JBIG2Bitmap-combine-Fix-crash-on-malformed-files.patch deleted file mode 100644 index 4cacfe7..0000000 --- a/debian/patches/0002-JBIG2Bitmap-combine-Fix-crash-on-malformed-files.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 70ceae2d090bbc9935d938e9e1475bcd57781f5e Mon Sep 17 00:00:00 2001 -From: Albert Astals Cid -Date: Tue, 17 Dec 2024 18:59:01 +0100 -Subject: JBIG2Bitmap::combine: Fix crash on malformed files - -Fixes #1553 ---- - poppler/JBIG2Stream.cc | 15 +++++++++------ - 1 file changed, 9 insertions(+), 6 deletions(-) - -diff --git a/poppler/JBIG2Stream.cc b/poppler/JBIG2Stream.cc -index 77ffeb28..bdc51d0d 100644 ---- a/poppler/JBIG2Stream.cc -+++ b/poppler/JBIG2Stream.cc -@@ -765,7 +765,7 @@ void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) - - void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp) - { -- int x0, x1, y0, y1, xx, yy; -+ int x0, x1, y0, y1, xx, yy, yyy; - unsigned char *srcPtr, *destPtr; - unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3; - bool oneByte; -@@ -812,14 +812,17 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp - oneByte = x0 == ((x1 - 1) & ~7); - - for (yy = y0; yy < y1; ++yy) { -- if (unlikely((y + yy >= h) || (y + yy < 0))) { -+ if (unlikely(checkedAdd(y, yy, &yyy))) { -+ continue; -+ } -+ if (unlikely((yyy >= h) || (yyy < 0))) { - continue; - } - - // one byte per line -- need to mask both left and right side - if (oneByte) { - if (x >= 0) { -- destPtr = data + (y + yy) * line + (x >> 3); -+ destPtr = data + yyy * line + (x >> 3); - srcPtr = bitmap->data + yy * bitmap->line; - dest = *destPtr; - src1 = *srcPtr; -@@ -842,7 +845,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp - } - *destPtr = dest; - } else { -- destPtr = data + (y + yy) * line; -+ destPtr = data + yyy * line; - srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); - dest = *destPtr; - src1 = *srcPtr; -@@ -872,7 +875,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp - - // left-most byte - if (x >= 0) { -- destPtr = data + (y + yy) * line + (x >> 3); -+ destPtr = data + yyy * line + (x >> 3); - srcPtr = bitmap->data + yy * bitmap->line; - src1 = *srcPtr++; - dest = *destPtr; -@@ -896,7 +899,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp - *destPtr++ = dest; - xx = x0 + 8; - } else { -- destPtr = data + (y + yy) * line; -+ destPtr = data + yyy * line; - srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); - src1 = *srcPtr++; - xx = x0; --- -2.30.2 - diff --git a/debian/patches/0003-PSStack-roll-Protect-against-doing-int-INT_MIN.patch b/debian/patches/0003-PSStack-roll-Protect-against-doing-int-INT_MIN.patch deleted file mode 100644 index 96d0b9e..0000000 --- a/debian/patches/0003-PSStack-roll-Protect-against-doing-int-INT_MIN.patch +++ /dev/null @@ -1,25 +0,0 @@ -From a165d7c184a75c8511354c8972aa8176928e239b Mon Sep 17 00:00:00 2001 -From: Albert Astals Cid -Date: Mon, 24 Mar 2025 00:44:54 +0100 -Subject: PSStack::roll: Protect against doing int = -INT_MIN - ---- - poppler/Function.cc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/poppler/Function.cc b/poppler/Function.cc -index 043ae8e9..65888a03 100644 ---- a/poppler/Function.cc -+++ b/poppler/Function.cc -@@ -1066,7 +1066,7 @@ void PSStack::roll(int n, int j) - PSObject obj; - int i, k; - -- if (unlikely(n == 0)) { -+ if (unlikely(n == 0 || j == INT_MIN)) { - return; - } - if (j >= 0) { --- -2.30.2 - diff --git a/debian/patches/CVE-2024-6239.patch b/debian/patches/CVE-2024-6239.patch deleted file mode 100644 index 10b47c0..0000000 --- a/debian/patches/CVE-2024-6239.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 96d64f89778aead72b8ef1b465f7db2a31ff5763 Mon Sep 17 00:00:00 2001 -From: zengwei -Date: Thu, 29 Jan 2026 11:35:54 +0800 -Subject: [PATCH] CVE-2024-6239 - ---- - utils/pdfinfo.cc | 33 ++++++++++++++------------------- - 1 file changed, 14 insertions(+), 19 deletions(-) - -diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc -index 1f4ca79..0268299 100644 ---- a/utils/pdfinfo.cc -+++ b/utils/pdfinfo.cc -@@ -15,7 +15,7 @@ - // under GPL version 2 or later - // - // Copyright (C) 2006 Dom Lachowicz --// Copyright (C) 2007-2010, 2012, 2016-2022 Albert Astals Cid -+// Copyright (C) 2007-2010, 2012, 2016-2022, 2024 Albert Astals Cid - // Copyright (C) 2010 Hib Eris - // Copyright (C) 2011 Vittal Aithal - // Copyright (C) 2012, 2013, 2016-2018, 2021 Adrian Johnson -@@ -112,11 +112,11 @@ static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to - { "-?", argFlag, &printHelp, 0, "print usage information" }, - {} }; - --static void printTextString(const GooString *s, const UnicodeMap *uMap) -+static void printStdTextString(const std::string &s, const UnicodeMap *uMap) - { - Unicode *u; - char buf[8]; -- int len = TextStringToUCS4(s->toStr(), &u); -+ int len = TextStringToUCS4(s, &u); - for (int i = 0; i < len; i++) { - int n = uMap->mapUnicode(u[i], buf, sizeof(buf)); - fwrite(buf, 1, n, stdout); -@@ -124,6 +124,11 @@ static void printTextString(const GooString *s, const UnicodeMap *uMap) - gfree(u); - } - -+static void printTextString(const GooString *s, const UnicodeMap *uMap) -+{ -+ printStdTextString(s->toStr(), uMap); -+} -+ - static void printUCS4String(const Unicode *u, int len, const UnicodeMap *uMap) - { - char buf[8]; -@@ -295,10 +300,6 @@ static void printStruct(const StructElement *element, unsigned indent) - } - } - --struct GooStringCompare --{ -- bool operator()(GooString *lhs, GooString *rhs) const { return lhs->cmp(const_cast(rhs)) < 0; } --}; - - static void printLinkDest(const std::unique_ptr &dest) - { -@@ -370,29 +371,25 @@ static void printLinkDest(const std::unique_ptr &dest) - - static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) - { -- std::map, GooStringCompare>> map; -+ std::map>> map; - - int numDests = doc->getCatalog()->numDestNameTree(); - for (int i = 0; i < numDests; i++) { -- GooString *name = new GooString(doc->getCatalog()->getDestNameTreeName(i)); -+ const GooString *name = doc->getCatalog()->getDestNameTreeName(i); - std::unique_ptr dest = doc->getCatalog()->getDestNameTreeDest(i); -- if (dest && dest->isPageRef()) { -+ if (name && dest && dest->isPageRef()) { - Ref pageRef = dest->getPageRef(); -- map[pageRef].insert(std::make_pair(name, std::move(dest))); -- } else { -- delete name; -+ map[pageRef].insert(std::make_pair(name->toStr(), std::move(dest))); - } - } - - numDests = doc->getCatalog()->numDests(); - for (int i = 0; i < numDests; i++) { -- GooString *name = new GooString(doc->getCatalog()->getDestsName(i)); -+ const char *name = doc->getCatalog()->getDestsName(i); - std::unique_ptr dest = doc->getCatalog()->getDestsDest(i); -- if (dest && dest->isPageRef()) { -+ if (name && dest && dest->isPageRef()) { - Ref pageRef = dest->getPageRef(); - map[pageRef].insert(std::make_pair(name, std::move(dest))); -- } else { -- delete name; - } - } - -@@ -406,9 +403,7 @@ static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) - printf("%4d ", i); - printLinkDest(it.second); - printf(" \""); -- printTextString(it.first, uMap); - printf("\"\n"); -- delete it.first; - } - } - } --- -2.20.1 - diff --git a/debian/patches/CVE-2025-32364.patch b/debian/patches/CVE-2025-32364.patch new file mode 100644 index 0000000..51914fd --- /dev/null +++ b/debian/patches/CVE-2025-32364.patch @@ -0,0 +1,31 @@ +From: Albert Astals Cid +Date: Mon, 24 Mar 2025 00:44:54 +0100 +Subject: [PATCH] PSStack::roll: Protect against doing int = -INT_MIN + +Origin: upstream 25.04 +--- + poppler/Function.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/poppler/Function.cc b/poppler/Function.cc +index d84c4e350..f3168f191 100644 +--- a/poppler/Function.cc ++++ b/poppler/Function.cc +@@ -13,7 +13,7 @@ + // All changes made under the Poppler project to this file are licensed + // under GPL version 2 or later + // +-// Copyright (C) 2006, 2008-2010, 2013-2015, 2017-2020, 2022-2024 Albert Astals Cid ++// Copyright (C) 2006, 2008-2010, 2013-2015, 2017-2020, 2022-2025 Albert Astals Cid + // Copyright (C) 2006 Jeff Muizelaar + // Copyright (C) 2010 Christian Feuersänger + // Copyright (C) 2011 Andrea Canciani +@@ -1068,7 +1068,7 @@ void PSStack::roll(int n, int j) + PSObject obj; + int i, k; + +- if (unlikely(n == 0)) { ++ if (unlikely(n == 0 || j == INT_MIN)) { + return; + } + if (j >= 0) { diff --git a/debian/patches/0004-Move-isOk-check-to-inside-JBIG2Bitmap-combine.patch b/debian/patches/CVE-2025-32365.patch similarity index 52% rename from debian/patches/0004-Move-isOk-check-to-inside-JBIG2Bitmap-combine.patch rename to debian/patches/CVE-2025-32365.patch index d3c9373..9320110 100644 --- a/debian/patches/0004-Move-isOk-check-to-inside-JBIG2Bitmap-combine.patch +++ b/debian/patches/CVE-2025-32365.patch @@ -1,17 +1,26 @@ -From d8aa58a1c931738bdba4ffaae1a80a4ecfbb36bf Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Mon, 31 Mar 2025 14:35:49 +0200 -Subject: Move isOk check to inside JBIG2Bitmap::combine +Subject: [PATCH] Move isOk check to inside JBIG2Bitmap::combine +Origin: upstream 25.04 --- - poppler/JBIG2Stream.cc | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) + poppler/JBIG2Stream.cc | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/poppler/JBIG2Stream.cc b/poppler/JBIG2Stream.cc -index bdc51d0d..3c92e707 100644 +index cf9e0c984..4e81d4a8c 100644 --- a/poppler/JBIG2Stream.cc +++ b/poppler/JBIG2Stream.cc -@@ -770,6 +770,9 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp +@@ -15,7 +15,7 @@ + // + // Copyright (C) 2006 Raj Kumar + // Copyright (C) 2006 Paul Walmsley +-// Copyright (C) 2006-2010, 2012, 2014-2022, 2024 Albert Astals Cid ++// Copyright (C) 2006-2010, 2012, 2014-2022, 2024, 2025 Albert Astals Cid + // Copyright (C) 2009 David Benjamin + // Copyright (C) 2011 Edward Jiang + // Copyright (C) 2012 William Bader +@@ -768,6 +768,9 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3; bool oneByte; @@ -21,7 +30,7 @@ index bdc51d0d..3c92e707 100644 // check for the pathological case where y = -2^31 if (y < -0x7fffffff) { return; -@@ -2200,9 +2203,7 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless +@@ -2198,9 +2201,7 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless if (pageH == 0xffffffff && y + h > curPageH) { pageBitmap->expand(y + h, pageDefPixel); } @@ -32,6 +41,3 @@ index bdc51d0d..3c92e707 100644 // store the region bitmap } else { --- -2.30.2 - diff --git a/debian/patches/CVE-2025-43903.patch b/debian/patches/CVE-2025-43903.patch new file mode 100644 index 0000000..b3ad504 --- /dev/null +++ b/debian/patches/CVE-2025-43903.patch @@ -0,0 +1,43 @@ +From: =?UTF-8?q?Juraj=20=C5=A0arinay?= +Date: Thu, 6 Mar 2025 02:02:56 +0100 +Subject: Properly verify adbe.pkcs7.sha1 signatures. + +For signatures with non-empty encapsulated content +(typically adbe.pkcs7.sha1), we only compared hash values and +never actually checked SignatureValue within SignerInfo. +The bug introduced by c7c0207b1cfe49a4353d6cda93dbebef4508138f +made trivial signature forgeries possible. Fix this by calling +NSS_CMSSignerInfo_Verify() after the hash values compare equal. + +Origin: upstream 25.04.0 +--- + poppler/NSSCryptoSignBackend.cc | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/poppler/NSSCryptoSignBackend.cc b/poppler/NSSCryptoSignBackend.cc +index 521137d6b..eeea26ee3 100644 +--- a/poppler/NSSCryptoSignBackend.cc ++++ b/poppler/NSSCryptoSignBackend.cc +@@ -877,13 +877,18 @@ SignatureValidationStatus NSSSignatureVerification::validateSignature() + This means it's not a detached type signature + so the digest is contained in SignedData->contentInfo + */ +- if (digest.len == content_info_data->len && memcmp(digest.data, content_info_data->data, digest.len) == 0) { +- return SIGNATURE_VALID; +- } else { ++ if (digest.len != content_info_data->len || memcmp(digest.data, content_info_data->data, digest.len) != 0) { + return SIGNATURE_DIGEST_MISMATCH; + } + +- } else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess) { ++ auto innerHashContext = HashContext::create(hashContext->getHashAlgorithm()); ++ innerHashContext->updateHash(content_info_data->data, content_info_data->len); ++ digest_buffer = innerHashContext->endHash(); ++ digest.data = digest_buffer.data(); ++ digest.len = digest_buffer.size(); ++ } ++ ++ if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess) { + return NSS_SigTranslate(CMSSignerInfo->verificationStatus); + } else { + return SIGNATURE_VALID; diff --git a/debian/patches/CVE-2025-50420.patch b/debian/patches/CVE-2025-50420.patch new file mode 100644 index 0000000..6afd927 --- /dev/null +++ b/debian/patches/CVE-2025-50420.patch @@ -0,0 +1,34 @@ +From 08d7894e4dd0e313c179e30f06ad8f546619b1b3 Mon Sep 17 00:00:00 2001 +From: Sune Vuorela +Date: Tue, 29 Jul 2025 14:14:00 +0200 +Subject: [PATCH] Fix crash in pdfseparate + +Don't continue recursing in PDFDoc::mark* if things looks a bit weirder +than expected +--- + poppler/PDFDoc.cc | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc +index 0b0b3215cb..0cfb273884 100644 +--- a/poppler/PDFDoc.cc ++++ b/poppler/PDFDoc.cc +@@ -1857,6 +1857,15 @@ bool PDFDoc::markAnnotations(Object *annotsObj, XRef *xRef, XRef *countRef, unsi + if (obj1.isDict()) { + Dict *dict = obj1.getDict(); + Object type = dict->lookup("Type"); ++ if (type.isNull()) { ++ Object subType = dict->lookup("SubType"); ++ // Type is optional, subtype is required ++ // If neither of them exists, something is probably ++ // weird here, so let us just skip this entry ++ if (subType.isNull()) { ++ continue; ++ } ++ } + if (type.isName() && strcmp(type.getName(), "Annot") == 0) { + const Object &obj2 = dict->lookupNF("P"); + if (obj2.isRef()) { +-- +GitLab + diff --git a/debian/patches/CVE-2025-52886.patch b/debian/patches/CVE-2025-52886.patch new file mode 100644 index 0000000..74fc723 --- /dev/null +++ b/debian/patches/CVE-2025-52886.patch @@ -0,0 +1,50 @@ +Backport of: + +From ac36affcc8486de38e8905a8d6547a3464ff46e5 Mon Sep 17 00:00:00 2001 +From: Sune Vuorela +Date: Tue, 3 Jun 2025 00:35:19 +0200 +Subject: [PATCH] Limit ammount of annots per document/page + +--- + poppler/Annot.cc | 4 ++++ + poppler/Page.cc | 16 ++++++++++++++++ + 2 files changed, 20 insertions(+) + +--- a/poppler/Annot.cc ++++ b/poppler/Annot.cc +@@ -7479,6 +7479,10 @@ Annots::Annots(PDFDoc *docA, int page, O + const Object &obj2 = annotsObj->arrayGetNF(i); + annot = createAnnot(std::move(obj1), &obj2); + if (annot) { ++ if (annot->refCnt > 100000) { ++ error(errSyntaxError, -1, "Annotations likely malformed. Too many references. Stopping processing annots on page {0:d}", page); ++ break; ++ } + if (annot->isOk()) { + annot->setPage(page, false); // Don't change /P + appendAnnot(annot); +--- a/poppler/Page.cc ++++ b/poppler/Page.cc +@@ -297,6 +297,22 @@ Page::Page(PDFDoc *docA, int numA, Objec + goto err2; + } + ++ if (annotsObj.isArray() && annotsObj.arrayGetLength() > 10000) { ++ error(errSyntaxError, -1, "Page annotations object (page {0:d}) is likely malformed. Too big: ({1:d})", num, annotsObj.arrayGetLength()); ++ goto err2; ++ } ++ if (annotsObj.isRef()) { ++ auto resolvedObj = getAnnotsObject(); ++ if (resolvedObj.isArray() && resolvedObj.arrayGetLength() > 10000) { ++ error(errSyntaxError, -1, "Page annotations object (page {0:d}) is likely malformed. Too big: ({1:d})", num, resolvedObj.arrayGetLength()); ++ goto err2; ++ } ++ if (!resolvedObj.isArray() && !resolvedObj.isNull()) { ++ error(errSyntaxError, -1, "Page annotations object (page {0:d}) is wrong type ({1:s})", num, resolvedObj.getTypeName()); ++ goto err2; ++ } ++ } ++ + // contents + contents = pageObj.dictLookupNF("Contents").copy(); + if (!(contents.isRef() || contents.isArray() || contents.isNull())) { diff --git a/debian/patches/series b/debian/patches/series index 3e14834..328f74e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,5 +1,5 @@ -0001-OutlineItem-open-Fix-crash-on-malformed-files.patch -0002-JBIG2Bitmap-combine-Fix-crash-on-malformed-files.patch -0003-PSStack-roll-Protect-against-doing-int-INT_MIN.patch -0004-Move-isOk-check-to-inside-JBIG2Bitmap-combine.patch -CVE-2024-6239.patch +CVE-2025-32364.patch +CVE-2025-32365.patch +CVE-2025-43903.patch +CVE-2025-52886.patch +CVE-2025-50420.patch diff --git a/debian/poppler-utils.install b/debian/poppler-utils.install index 63c303b..5142b74 100644 --- a/debian/poppler-utils.install +++ b/debian/poppler-utils.install @@ -1,2 +1,3 @@ usr/bin/ +usr/share/locale/*/*/pdf* usr/share/man/man1/ diff --git a/debian/rules b/debian/rules index 9d052c2..37c1e45 100755 --- a/debian/rules +++ b/debian/rules @@ -6,10 +6,12 @@ UPSTREAM_VERSION := $(shell dpkg-parsechangelog | sed -n 's/^Version: //p' | cut SYMBOLS_in := $(wildcard debian/*.symbols.in) SYMBOLS_generated := $(patsubst %.in,%,$(SYMBOLS_in)) -libpopplerN := $(shell sed -rn 's/^Package:[[:space:]]*(libpoppler[0-9]+)[[:space:]]*$$/\1/p' debian/control) +libpopplerN := $(shell sed -rn 's/^Package:[[:space:]]*(libpoppler[0-9]+(t64)?)[[:space:]]*$$/\1/p' debian/control) + +built_binaries := $(shell dh_listpackages) # enable all the hardening options -export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_BUILD_MAINT_OPTIONS = hardening=+all optimize=+lto # disable gtk stuff to minimize Build-Depends CONFIGURE_ARGS := \ @@ -18,14 +20,17 @@ CONFIGURE_ARGS := \ -DCMAKE_CXX_FLAGS_RELEASE="$(CXXFLAGS)" \ -DENABLE_GLIB=ON \ -DENABLE_QT5=ON \ - -DENABLE_QT6=ON \ -DENABLE_CPP=ON \ - -DENABLE_GTK_DOC=ON \ + -DENABLE_GTK_DOC=$(if $(filter libpoppler-glib-doc,$(shell dh_listpackages)),ON,OFF) \ -DENABLE_UNSTABLE_API_ABI_HEADERS=ON \ -DENABLE_CMS=lcms2 \ -DENABLE_LIBOPENJPEG=openjpeg2 \ -DBUILD_GTK_TESTS=OFF +ifeq ($(filter %-qt6-dev,$(built_binaries)),) +CONFIGURE_ARGS += -DENABLE_QT6=OFF +endif + %: dh $@ diff --git a/debian/tests/control b/debian/tests/control index 311792f..5a4e8f2 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -1,11 +1,12 @@ Tests: cpp -Depends: build-essential, pkg-config, libpoppler-cpp-dev, cups-filters:native +Depends: build-essential, cups-filters:native, libpoppler-cpp-dev, pkgconf Tests: glib -Depends: build-essential, pkg-config, libpoppler-glib-dev, cups-filters:native +Depends: build-essential, cups-filters:native, libpoppler-glib-dev, pkgconf Tests: qt5 -Depends: build-essential, pkg-config, libpoppler-qt5-dev, cups-filters:native +Depends: build-essential, cups-filters:native, libpoppler-qt5-dev, pkgconf Tests: qt6 -Depends: build-essential, pkg-config, libpoppler-qt6-dev, cups-filters:native +Architecture: hurd-any alpha amd64 arm64 armel armhf hppa loong64 m68k mips64el powerpc ppc64 ppc64el riscv64 s390x sh4 sparc64 +Depends: build-essential, cups-filters:native, libpoppler-qt6-dev, pkgconf diff --git a/debian/upstream/metadata b/debian/upstream/metadata index 7ef12e9..c9bbe4f 100644 --- a/debian/upstream/metadata +++ b/debian/upstream/metadata @@ -1,3 +1,4 @@ +--- Bug-Database: https://gitlab.freedesktop.org/poppler/poppler/-/issues Bug-Submit: https://gitlab.freedesktop.org/poppler/poppler/-/issues/new Repository: https://gitlab.freedesktop.org/poppler/poppler.git diff --git a/debian/watch b/debian/watch index e28f36c..5f9e548 100644 --- a/debian/watch +++ b/debian/watch @@ -1,2 +1,2 @@ version=4 -opts="pgpmode=auto" https://poppler.freedesktop.org/ @PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@ +opts="pgpmode=auto" https://poppler.freedesktop.org/releases.html @PACKAGE@@ANY_VERSION@@ARCHIVE_EXT@ diff --git a/do-the-gnupg-2.4-dance.sh b/do-the-gnupg-2.4-dance.sh new file mode 100644 index 0000000..9bd08ee --- /dev/null +++ b/do-the-gnupg-2.4-dance.sh @@ -0,0 +1,51 @@ +#! /bin/sh +set -eux + +if [ -z ${1} ] +then + echo "Destination must be provided" + exit 1 +fi + +apt-get -y install --no-install-recommends libksba-dev libgpg-error-dev libgcrypt-dev libassuan-dev libnpth-dev libgnutls28-dev pkg-config libldap-dev wget ca-certificates bzip2 patch texinfo + +DESTINATION=${1} +if [ -e "${DESTINATION}/bin/gpg" ] +then + echo "Already installed" + exit 0 +fi + +if [ -e "${DESTINATION}" ] +then + echo "Please use a nonexisting destination" + exit 1 +fi + +GNUPG_VERSION=2.4.4 +GPGME_VERSION=1.22.0 + +WORKDIR=$(mktemp -d) + +cd ${WORKDIR} + +wget https://gnupg.org/ftp/gcrypt/gnupg/gnupg-${GNUPG_VERSION}.tar.bz2 +tar xf gnupg-${GNUPG_VERSION}.tar.bz2 +wget https://gnupg.org/ftp/gcrypt/gpgme/gpgme-${GPGME_VERSION}.tar.bz2 +tar xf gpgme-${GPGME_VERSION}.tar.bz2 + + +mkdir -p ${WORKDIR}/gnupg-${GNUPG_VERSION}/build +cd gnupg-${GNUPG_VERSION} +cd build +../configure --prefix=${DESTINATION} +make install + +cd ${WORKDIR} + +mkdir gpgme-${GPGME_VERSION}/build +cd gpgme-${GPGME_VERSION}/build +../configure --prefix=${DESTINATION} --enable-fixed-path=${DESTINATION}/bin --enable-languages=cpp +PATH=${DESTINATION}/bin:$PATH make -j5 install + + diff --git a/fofi/FoFiBase.h b/fofi/FoFiBase.h index ca62e94..2df53fe 100644 --- a/fofi/FoFiBase.h +++ b/fofi/FoFiBase.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2018, 2022 Albert Astals Cid +// Copyright (C) 2018, 2022, 2024 Albert Astals Cid // Copyright (C) 2022 Oliver Sander // // To see a description of the changes please see the Changelog file that @@ -24,6 +24,10 @@ #ifndef FOFIBASE_H #define FOFIBASE_H +#include "poppler_private_export.h" + +#include + //------------------------------------------------------------------------ using FoFiOutputFunc = void (*)(void *stream, const char *data, size_t len); diff --git a/fofi/FoFiIdentifier.cc b/fofi/FoFiIdentifier.cc index ec92b33..7d65b45 100644 --- a/fofi/FoFiIdentifier.cc +++ b/fofi/FoFiIdentifier.cc @@ -41,7 +41,7 @@ class Reader Reader(const Reader &) = delete; Reader &operator=(const Reader &other) = delete; - virtual ~Reader() { } + virtual ~Reader() = default; // Read one byte. Returns -1 if past EOF. virtual int getByte(int pos) = 0; @@ -98,7 +98,7 @@ MemReader::MemReader(const char *bufA, int lenA) len = lenA; } -MemReader::~MemReader() { } +MemReader::~MemReader() = default; int MemReader::getByte(int pos) { @@ -323,7 +323,7 @@ StreamReader::StreamReader(int (*getCharA)(void *data), void *dataA) bufLen = 0; } -StreamReader::~StreamReader() { } +StreamReader::~StreamReader() = default; int StreamReader::getByte(int pos) { diff --git a/fofi/FoFiTrueType.cc b/fofi/FoFiTrueType.cc index 020a374..bdf13bd 100644 --- a/fofi/FoFiTrueType.cc +++ b/fofi/FoFiTrueType.cc @@ -16,7 +16,7 @@ // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2007 Koji Otani // Copyright (C) 2007 Carlos Garcia Campos -// Copyright (C) 2008, 2009, 2012, 2014-2022 Albert Astals Cid +// Copyright (C) 2008, 2009, 2012, 2014-2022, 2024 Albert Astals Cid // Copyright (C) 2008 Tomas Are Haavet // Copyright (C) 2012 Suzuki Toshiya // Copyright (C) 2012, 2017 Adrian Johnson @@ -25,7 +25,8 @@ // Copyright (C) 2015, 2016 William Bader // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 Zachary Travis -// Copyright (C) 2022 Oliver Sander +// Copyright (C) 2022, 2024 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -38,9 +39,10 @@ #include #include #include -#include "goo/gmem.h" +#include #include "goo/GooLikely.h" #include "goo/GooString.h" +#include "goo/GooCheckedOps.h" #include "FoFiType1C.h" #include "FoFiTrueType.h" #include "poppler/Error.h" @@ -109,28 +111,28 @@ struct TrueTypeTable { - unsigned int tag; - unsigned int checksum; - int offset; - int origOffset; - int len; + unsigned int tag = 0; + unsigned int checksum = 0; + int offset = 0; + int origOffset = 0; + int len = 0; }; struct TrueTypeCmap { - int platform; - int encoding; - int offset; - int len; - int fmt; + int platform = 0; + int encoding = 0; + int offset = 0; + int len = 0; + int fmt = 0; }; struct TrueTypeLoca { - int idx; - int origOffset; - int newOffset; - int len; + int idx = 0; + int origOffset = 0; + int newOffset = 0; + int len = 0; }; #define cmapTag 0x636d6170 @@ -482,10 +484,6 @@ std::unique_ptr FoFiTrueType::load(const char *fileName, int faceI FoFiTrueType::FoFiTrueType(const unsigned char *fileA, int lenA, bool freeFileDataA, int faceIndexA) : FoFiBase(fileA, lenA, freeFileDataA) { - tables = nullptr; - nTables = 0; - cmaps = nullptr; - nCmaps = 0; parsedOk = false; faceIndex = faceIndexA; gsubFeatureTable = 0; @@ -494,15 +492,11 @@ FoFiTrueType::FoFiTrueType(const unsigned char *fileA, int lenA, bool freeFileDa parse(); } -FoFiTrueType::~FoFiTrueType() -{ - gfree(tables); - gfree(cmaps); -} +FoFiTrueType::~FoFiTrueType() = default; int FoFiTrueType::getNumCmaps() const { - return nCmaps; + return cmaps.size(); } int FoFiTrueType::getCmapPlatform(int i) const @@ -517,9 +511,7 @@ int FoFiTrueType::getCmapEncoding(int i) const int FoFiTrueType::findCmap(int platform, int encoding) const { - int i; - - for (i = 0; i < nCmaps; ++i) { + for (int i = 0; i < (int)cmaps.size(); ++i) { if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) { return i; } @@ -536,7 +528,7 @@ int FoFiTrueType::mapCodeToGID(int i, unsigned int c) const unsigned int high, low, idx; bool ok; - if (i < 0 || i >= nCmaps) { + if (i < 0 || i >= (int)cmaps.size()) { return 0; } ok = true; @@ -655,12 +647,10 @@ int FoFiTrueType::mapNameToGID(const char *name) const bool FoFiTrueType::getCFFBlock(char **start, int *length) const { - int i; - - if (!openTypeCFF || !tables) { + if (!openTypeCFF || tables.empty()) { return false; } - i = seekTable("CFF "); + int i = seekTable("CFF "); if (i < 0 || !checkRegion(tables[i].offset, tables[i].len)) { return false; } @@ -669,21 +659,19 @@ bool FoFiTrueType::getCFFBlock(char **start, int *length) const return true; } -int *FoFiTrueType::getCIDToGIDMap(int *nCIDs) const +std::vector FoFiTrueType::getCIDToGIDMap() const { char *start; int length; FoFiType1C *ff; - int *map; - *nCIDs = 0; if (!getCFFBlock(&start, &length)) { - return nullptr; + return {}; } if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { - return nullptr; + return {}; } - map = ff->getCIDToGIDMap(nCIDs); + std::vector map = ff->getCIDToGIDMap(); delete ff; return map; } @@ -729,7 +717,7 @@ void FoFiTrueType::getFontMatrix(double *mat) const delete ff; } -void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::convertToType42(const char *psName, char **encoding, const std::vector &codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const { int maxUsedGlyph; bool ok; @@ -740,8 +728,8 @@ void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *cod // write the header ok = true; - std::unique_ptr buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); // begin the font dictionary (*outputFunc)(outputStream, "10 dict begin\n", 14); @@ -751,7 +739,7 @@ void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *cod (*outputFunc)(outputStream, "/FontType 42 def\n", 17); (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); // write the guts of the dictionary @@ -779,7 +767,7 @@ void FoFiTrueType::convertToType1(const char *psName, const char **newEncoding, delete ff; } -void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::convertToCIDType2(const char *psName, const std::vector &cidMap, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const { int cid, maxUsedGlyph; bool ok; @@ -791,8 +779,8 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int // write the header ok = true; - std::unique_ptr buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format("%!PS-TrueTypeFont-{0:2g}\n", (double)getS32BE(0, &ok) / 65536.0); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); // begin the font dictionary (*outputFunc)(outputStream, "20 dict begin\n", 14); @@ -807,19 +795,19 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int (*outputFunc)(outputStream, " /Supplement 0 def\n", 20); (*outputFunc)(outputStream, " end def\n", 10); (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15); - if (cidMap) { - buf = GooString::format("/CIDCount {0:d} def\n", nCIDs); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); - if (nCIDs > 32767) { + if (!cidMap.empty()) { + buf = GooString::format("/CIDCount {0:d} def\n", int(cidMap.size())); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); + if (cidMap.size() > 32767) { (*outputFunc)(outputStream, "/CIDMap [", 9); - for (i = 0; i < nCIDs; i += 32768 - 16) { + for (i = 0; i < int(cidMap.size()); i += 32768 - 16) { (*outputFunc)(outputStream, "<\n", 2); - for (j = 0; j < 32768 - 16 && i + j < nCIDs; j += 16) { + for (j = 0; j < 32768 - 16 && i + j < int(cidMap.size()); j += 16) { (*outputFunc)(outputStream, " ", 2); - for (k = 0; k < 16 && i + j + k < nCIDs; ++k) { + for (k = 0; k < 16 && i + j + k < int(cidMap.size()); ++k) { cid = cidMap[i + j + k]; buf = GooString::format("{0:02x}{1:02x}", (cid >> 8) & 0xff, cid & 0xff); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "\n", 1); } @@ -829,12 +817,12 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int (*outputFunc)(outputStream, "] def\n", 6); } else { (*outputFunc)(outputStream, "/CIDMap <\n", 10); - for (i = 0; i < nCIDs; i += 16) { + for (i = 0; i < int(cidMap.size()); i += 16) { (*outputFunc)(outputStream, " ", 2); - for (j = 0; j < 16 && i + j < nCIDs; ++j) { + for (j = 0; j < 16 && i + j < int(cidMap.size()); ++j) { cid = cidMap[i + j]; buf = GooString::format("{0:02x}{1:02x}", (cid >> 8) & 0xff, cid & 0xff); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "\n", 1); } @@ -843,27 +831,27 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int } else { // direct mapping - just fill the string(s) with s[i]=i buf = GooString::format("/CIDCount {0:d} def\n", nGlyphs); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); if (nGlyphs > 32767) { (*outputFunc)(outputStream, "/CIDMap [\n", 10); for (i = 0; i < nGlyphs; i += 32767) { j = nGlyphs - i < 32767 ? nGlyphs - i : 32767; buf = GooString::format(" {0:d} string 0 1 {1:d} {{\n", 2 * j, j - 1); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format(" 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n", i); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format(" 1 index exch dup 2 mul 1 add exch {0:d} add" " 255 and put\n", i); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, " } for\n", 8); } (*outputFunc)(outputStream, "] def\n", 6); } else { buf = GooString::format("/CIDMap {0:d} string\n", 2 * nGlyphs); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format(" 0 1 {0:d} {{\n", nGlyphs - 1); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, " 2 copy dup 2 mul exch -8 bitshift put\n", 42); (*outputFunc)(outputStream, " 1 index exch dup 2 mul 1 add exch 255 and put\n", 50); (*outputFunc)(outputStream, " } for\n", 8); @@ -872,7 +860,7 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int } (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26); (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30); @@ -886,7 +874,7 @@ void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int (*outputFunc)(outputStream, "CIDFontName currentdict end /CIDFont defineresource pop\n", 56); } -void FoFiTrueType::convertToCIDType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::convertToCIDType0(const char *psName, const std::vector &cidMap, FoFiOutputFunc outputFunc, void *outputStream) const { char *start; int length; @@ -898,11 +886,11 @@ void FoFiTrueType::convertToCIDType0(const char *psName, int *cidMap, int nCIDs, if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } - ff->convertToCIDType0(psName, cidMap, nCIDs, outputFunc, outputStream); + ff->convertToCIDType0(psName, cidMap, outputFunc, outputStream); delete ff; } -void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::convertToType0(const char *psName, const std::vector &cidMap, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const { GooString *sfntsName; int maxUsedGlyph, n, i, j; @@ -936,8 +924,8 @@ void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bo // referencing zero-length glyphs that we trimmed. // This allows pdftops to avoid writing huge files while still // handling the rare PDF that uses a zero-length glyph. - if (cidMap) { - n = nCIDs; + if (!cidMap.empty()) { + n = cidMap.size(); } else if (nGlyphs > maxUsedGlyph + 256) { if (maxUsedGlyph <= 255) { n = 256; @@ -952,12 +940,12 @@ void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bo (*outputFunc)(outputStream, "10 dict begin\n", 14); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, strlen(psName)); - std::unique_ptr buf = GooString::format("_{0:02x} def\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format("_{0:02x} def\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FontType 42 def\n", 17); (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); buf = GooString::format("/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n", bbox[0], bbox[1], bbox[2], bbox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); (*outputFunc)(outputStream, "/sfnts ", 7); (*outputFunc)(outputStream, psName, strlen(psName)); @@ -965,14 +953,14 @@ void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bo (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); for (j = 0; j < 256 && i + j < n; ++j) { buf = GooString::format("dup {0:d} /c{1:02x} put\n", j, j); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "readonly def\n", 13); (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32); (*outputFunc)(outputStream, "/.notdef 0 def\n", 15); for (j = 0; j < 256 && i + j < n; ++j) { - buf = GooString::format("/c{0:02x} {1:d} def\n", j, cidMap ? cidMap[i + j] : i + j); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + buf = GooString::format("/c{0:02x} {1:d} def\n", j, !cidMap.empty() ? cidMap[i + j] : i + j); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "end readonly def\n", 17); (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); @@ -988,22 +976,22 @@ void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bo (*outputFunc)(outputStream, "/FMapType 2 def\n", 16); (*outputFunc)(outputStream, "/Encoding [\n", 12); for (i = 0; i < n; i += 256) { - const std::unique_ptr buf = GooString::format("{0:d}\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("{0:d}\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); (*outputFunc)(outputStream, "/FDepVector [\n", 14); for (i = 0; i < n; i += 256) { (*outputFunc)(outputStream, "/", 1); (*outputFunc)(outputStream, psName, strlen(psName)); - const std::unique_ptr buf = GooString::format("_{0:02x} findfont\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("_{0:02x} findfont\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); } -void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::convertToType0(const char *psName, const std::vector &cidMap, FoFiOutputFunc outputFunc, void *outputStream) const { char *start; int length; @@ -1015,7 +1003,7 @@ void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, Fo if (!(ff = FoFiType1C::make((unsigned char *)start, length))) { return; } - ff->convertToType0(psName, cidMap, nCIDs, outputFunc, outputStream); + ff->convertToType0(psName, cidMap, outputFunc, outputStream); delete ff; } @@ -1030,21 +1018,21 @@ void FoFiTrueType::cvtEncoding(char **encoding, FoFiOutputFunc outputFunc, void if (!(name = encoding[i])) { name = ".notdef"; } - const std::unique_ptr buf = GooString::format("dup {0:d} /", i); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("dup {0:d} /", i); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, name, strlen(name)); (*outputFunc)(outputStream, " put\n", 5); } } else { for (i = 0; i < 256; ++i) { - const std::unique_ptr buf = GooString::format("dup {0:d} /c{1:02x} put\n", i, i); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("dup {0:d} /c{1:02x} put\n", i, i); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } } (*outputFunc)(outputStream, "readonly def\n", 13); } -void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::cvtCharStrings(char **encoding, const std::vector &codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const { const char *name; char buf2[16]; @@ -1055,7 +1043,7 @@ void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOut (*outputFunc)(outputStream, "/.notdef 0 def\n", 15); // if there's no 'cmap' table, punt - if (nCmaps == 0) { + if (cmaps.empty()) { goto err; } @@ -1073,7 +1061,7 @@ void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOut sprintf(buf2, "c%02x", i); name = buf2; } - if (name && strcmp(name, ".notdef")) { + if (name && (strcmp(name, ".notdef") != 0)) { k = codeToGID[i]; // note: Distiller (maybe Adobe's PS interpreter in general) // doesn't like TrueType fonts that have CharStrings entries @@ -1082,8 +1070,8 @@ void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOut if (k > 0 && k < nGlyphs) { (*outputFunc)(outputStream, "/", 1); (*outputFunc)(outputStream, name, strlen(name)); - const std::unique_ptr buf = GooString::format(" {0:d} def\n", k); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format(" {0:d} def\n", k); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } } } @@ -1094,16 +1082,15 @@ void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOut void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const GooString *name, bool needVerticalMetrics, int *maxUsedGlyph) const { - unsigned char headData[54]; - TrueTypeLoca *locaTable; - unsigned char *locaData; + std::array headData; + std::vector locaData; TrueTypeTable newTables[nT42Tables]; unsigned char tableDir[12 + nT42Tables * 16]; bool ok; unsigned int checksum; int nNewTables; - int glyfTableLen, length, pos, glyfPos, i, j, k, vmtxTabLength; - unsigned char vheaTab[36] = { + int glyfTableLen, length, glyfPos, j, k; + std::array vheaTab = { 0, 1, 0, 0, // table version number 0, 0, // ascent 0, 0, // descent @@ -1122,22 +1109,22 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const 0, 0, // metric data format 0, 1 // number of advance heights in vmtx table }; - unsigned char *vmtxTab; + std::vector vmtxTab; bool needVhea, needVmtx; int advance; *maxUsedGlyph = -1; // construct the 'head' table, zero out the font checksum - i = seekTable("head"); - if (i < 0 || i >= nTables) { + int i = seekTable("head"); + if (i < 0 || i >= (int)tables.size()) { return; } - pos = tables[i].offset; + int pos = tables[i].offset; if (!checkRegion(pos, 54)) { return; } - memcpy(headData, file + pos, 54); + memcpy(headData.data(), file + pos, 54); headData[8] = headData[9] = headData[10] = headData[11] = (unsigned char)0; // check for a bogus loca format field in the 'head' table @@ -1154,7 +1141,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const // table, cmpTrueTypeLocaOffset uses offset as its primary sort key, // and idx as its secondary key (ensuring that adjacent entries with // the same pos value remain in the same order) - locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca)); + std::vector locaTable { size_t(nGlyphs + 1) }; i = seekTable("loca"); pos = tables[i].offset; i = seekTable("glyf"); @@ -1171,12 +1158,12 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const locaTable[i].origOffset = glyfTableLen; } } - std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaOffsetFunctor()); + std::ranges::sort(locaTable, cmpTrueTypeLocaOffsetFunctor()); for (i = 0; i < nGlyphs; ++i) { locaTable[i].len = locaTable[i + 1].origOffset - locaTable[i].origOffset; } locaTable[nGlyphs].len = 0; - std::sort(locaTable, locaTable + nGlyphs + 1, cmpTrueTypeLocaIdxFunctor()); + std::ranges::sort(locaTable, cmpTrueTypeLocaIdxFunctor()); pos = 0; for (i = 0; i <= nGlyphs; ++i) { locaTable[i].newOffset = pos; @@ -1196,7 +1183,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const } // construct the new 'loca' table - locaData = (unsigned char *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2)); + locaData.resize((nGlyphs + 1) * (locaFmt ? 4 : 2)); for (i = 0; i <= nGlyphs; ++i) { pos = locaTable[i].newOffset; if (locaFmt) { @@ -1217,8 +1204,6 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const ++nNewTables; } } - vmtxTab = nullptr; // make gcc happy - vmtxTabLength = 0; advance = 0; // make gcc happy if (needVerticalMetrics) { needVhea = seekTable("vhea") < 0; @@ -1244,10 +1229,10 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const checksum = 0; // make gcc happy if (i == t42HeadTable) { length = 54; - checksum = computeTableChecksum(headData, 54); + checksum = computeTableChecksum(headData); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); - checksum = computeTableChecksum(locaData, length); + checksum = computeTableChecksum(locaData); } else if (i == t42GlyfTable) { length = 0; checksum = 0; @@ -1258,31 +1243,26 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const length += 4 - (length & 3); } if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { - checksum += computeTableChecksum(file + glyfPos + locaTable[j].origOffset, locaTable[j].len); + checksum += computeTableChecksum(std::span(file + glyfPos + locaTable[j].origOffset, locaTable[j].len)); } } } else { if ((j = seekTable(t42Tables[i].tag)) >= 0) { length = tables[j].len; if (checkRegion(tables[j].offset, length)) { - checksum = computeTableChecksum(file + tables[j].offset, length); + checksum = computeTableChecksum(std::span(file + tables[j].offset, length)); } } else if (needVerticalMetrics && i == t42VheaTable) { vheaTab[10] = advance / 256; // max advance height vheaTab[11] = advance % 256; - length = sizeof(vheaTab); - checksum = computeTableChecksum(vheaTab, length); + length = vheaTab.size(); + checksum = computeTableChecksum(vheaTab); } else if (needVerticalMetrics && i == t42VmtxTable) { length = 4 + (nGlyphs - 1) * 2; - vmtxTabLength = length; - vmtxTab = (unsigned char *)gmalloc(length); + vmtxTab.resize(length, 0); vmtxTab[0] = advance / 256; vmtxTab[1] = advance % 256; - for (j = 2; j < length; j += 2) { - vmtxTab[j] = 0; - vmtxTab[j + 1] = 0; - } - checksum = computeTableChecksum(vmtxTab, length); + checksum = computeTableChecksum(vmtxTab); } else if (t42Tables[i].required) { //~ error(-1, "Embedded TrueType font is missing a required table ('%s')", //~ t42Tables[i].tag); @@ -1347,7 +1327,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const } // compute the font checksum and store it in the head table - checksum = computeTableChecksum(tableDir, 12 + nNewTables * 16); + checksum = computeTableChecksum(std::span(tableDir, 12 + nNewTables * 16)); for (i = 0; i < nNewTables; ++i) { checksum += newTables[i].checksum; } @@ -1367,20 +1347,20 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const } // write the table directory - dumpString(tableDir, 12 + nNewTables * 16, outputFunc, outputStream); + dumpString(std::span(tableDir, 12 + nNewTables * 16), outputFunc, outputStream); // write the tables for (i = 0; i < nNewTables; ++i) { if (i == t42HeadTable) { - dumpString(headData, 54, outputFunc, outputStream); + dumpString(headData, outputFunc, outputStream); } else if (i == t42LocaTable) { length = (nGlyphs + 1) * (locaFmt ? 4 : 2); - dumpString(locaData, length, outputFunc, outputStream); + dumpString(locaData, outputFunc, outputStream); } else if (i == t42GlyfTable) { glyfPos = tables[seekTable("glyf")].offset; for (j = 0; j < nGlyphs; ++j) { if (locaTable[j].len > 0 && checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { - dumpString(file + glyfPos + locaTable[j].origOffset, locaTable[j].len, outputFunc, outputStream); + dumpString(std::span(file + glyfPos + locaTable[j].origOffset, locaTable[j].len), outputFunc, outputStream); } } } else { @@ -1389,19 +1369,18 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const // headers if ((length = newTables[i].len) > 0) { if ((j = seekTable(t42Tables[i].tag)) >= 0 && checkRegion(tables[j].offset, tables[j].len)) { - dumpString(file + tables[j].offset, tables[j].len, outputFunc, outputStream); + dumpString(std::span(file + tables[j].offset, tables[j].len), outputFunc, outputStream); } else if (needVerticalMetrics && i == t42VheaTable) { if (unlikely(length > (int)sizeof(vheaTab))) { error(errSyntaxWarning, -1, "length bigger than vheaTab size"); length = sizeof(vheaTab); } - dumpString(vheaTab, length, outputFunc, outputStream); + dumpString(vheaTab, outputFunc, outputStream); } else if (needVerticalMetrics && i == t42VmtxTable) { - if (unlikely(length > vmtxTabLength)) { + if (unlikely(length > (int)vmtxTab.size())) { error(errSyntaxWarning, -1, "length bigger than vmtxTab size"); - length = vmtxTabLength; } - dumpString(vmtxTab, length, outputFunc, outputStream); + dumpString(vmtxTab, outputFunc, outputStream); } } } @@ -1409,33 +1388,25 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const // end the sfnts array (*outputFunc)(outputStream, "] def\n", 6); - - gfree(locaData); - gfree(locaTable); - if (vmtxTab) { - gfree(vmtxTab); - } } -void FoFiTrueType::dumpString(const unsigned char *s, int length, FoFiOutputFunc outputFunc, void *outputStream) const +void FoFiTrueType::dumpString(std::span s, FoFiOutputFunc outputFunc, void *outputStream) { - int pad, i, j; - (*outputFunc)(outputStream, "<", 1); - for (i = 0; i < length; i += 32) { - for (j = 0; j < 32 && i + j < length; ++j) { - const std::unique_ptr buf = GooString::format("{0:02x}", s[i + j] & 0xff); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + for (int i = 0; i < (int)s.size(); i += 32) { + for (int j = 0; j < 32 && i + j < (int)s.size(); ++j) { + const std::string buf = GooString::format("{0:02x}", s[i + j] & 0xff); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (i % (65536 - 32) == 65536 - 64) { (*outputFunc)(outputStream, ">\n<", 3); - } else if (i + 32 < length) { + } else if (i + 32 < (int)s.size()) { (*outputFunc)(outputStream, "\n", 1); } } - if (length & 3) { - pad = 4 - (length & 3); - for (i = 0; i < pad; ++i) { + if (s.size() & 3) { + int pad = 4 - (s.size() & 3); + for (int i = 0; i < pad; ++i) { (*outputFunc)(outputStream, "00", 2); } } @@ -1443,20 +1414,17 @@ void FoFiTrueType::dumpString(const unsigned char *s, int length, FoFiOutputFunc (*outputFunc)(outputStream, "00>\n", 4); } -unsigned int FoFiTrueType::computeTableChecksum(const unsigned char *data, int length) const +unsigned int FoFiTrueType::computeTableChecksum(std::span data) { - unsigned int checksum, word; - int i; - - checksum = 0; - for (i = 0; i + 3 < length; i += 4) { - word = ((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + (data[i + 3] & 0xff); + unsigned checksum = 0; + for (size_t i = 0; i + 3 < data.size(); i += 4) { + unsigned int word = ((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + (data[i + 3] & 0xff); checksum += word; } - if (length & 3) { - word = 0; - i = length & ~3; - switch (length & 3) { + if (data.size() & 3) { + unsigned int word = 0; + int i = data.size() & ~3; + switch (data.size() & 3) { case 3: word |= (data[i + 2] & 0xff) << 8; // fallthrough @@ -1516,11 +1484,11 @@ void FoFiTrueType::parse() openTypeCFF = ver == 0x4f54544f; // 'OTTO' // read the table directory - nTables = getU16BE(pos + 4, &parsedOk); + int nTables = getU16BE(pos + 4, &parsedOk); if (!parsedOk) { return; } - tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable)); + tables.resize(nTables); pos += 12; j = 0; for (i = 0; i < nTables; ++i) { @@ -1537,9 +1505,9 @@ void FoFiTrueType::parse() } if (nTables != j) { nTables = j; - tables = (TrueTypeTable *)greallocn_checkoverflow(tables, nTables, sizeof(TrueTypeTable)); + tables.resize(nTables); } - if (!parsedOk || tables == nullptr) { + if (!parsedOk || tables.empty()) { parsedOk = false; return; } @@ -1554,12 +1522,12 @@ void FoFiTrueType::parse() // read the cmaps if ((i = seekTable("cmap")) >= 0) { pos = tables[i].offset + 2; - nCmaps = getU16BE(pos, &parsedOk); + int nCmaps = getU16BE(pos, &parsedOk); pos += 2; if (!parsedOk) { return; } - cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap)); + cmaps.resize(nCmaps); for (j = 0; j < nCmaps; ++j) { cmaps[j].platform = getU16BE(pos, &parsedOk); cmaps[j].encoding = getU16BE(pos + 2, &parsedOk); @@ -1569,10 +1537,11 @@ void FoFiTrueType::parse() cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk); } if (!parsedOk) { + cmaps.clear(); return; } } else { - nCmaps = 0; + cmaps.clear(); } // get the number of glyphs from the maxp table @@ -1676,10 +1645,9 @@ void FoFiTrueType::readPostTable() int FoFiTrueType::seekTable(const char *tag) const { unsigned int tagI; - int i; tagI = ((tag[0] & 0xff) << 24) | ((tag[1] & 0xff) << 16) | ((tag[2] & 0xff) << 8) | (tag[3] & 0xff); - for (i = 0; i < nTables; ++i) { + for (int i = 0; i < (int)tables.size(); ++i) { if (tables[i].tag == tagI) { return i; } diff --git a/fofi/FoFiTrueType.h b/fofi/FoFiTrueType.h index f98244a..3c679f0 100644 --- a/fofi/FoFiTrueType.h +++ b/fofi/FoFiTrueType.h @@ -15,11 +15,12 @@ // // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2007 Koji Otani -// Copyright (C) 2011, 2012, 2018-2020 Albert Astals Cid +// Copyright (C) 2011, 2012, 2018-2020, 2024 Albert Astals Cid // Copyright (C) 2012 Suzuki Toshiya // Copyright (C) 2016 William Bader // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -31,8 +32,10 @@ #include #include +#include #include #include +#include #include "FoFiBase.h" #include "poppler_private_export.h" @@ -48,10 +51,10 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase { public: // Create a FoFiTrueType object from a memory buffer. - static std::unique_ptr make(const unsigned char *fileA, int lenA, int faceIndexA = 0); + static std::unique_ptr make(const unsigned char *fileA, int lenA, int faceIndexA); // Create a FoFiTrueType object from a file on disk. - static std::unique_ptr load(const char *fileName, int faceIndexA = 0); + static std::unique_ptr load(const char *fileName, int faceIndexA); ~FoFiTrueType() override; @@ -84,10 +87,10 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase // font does not have a post table. int mapNameToGID(const char *name) const; - // Return the mapping from CIDs to GIDs, and return the number of - // CIDs in *. This is only useful for CID fonts. (Only + // Return the mapping from CIDs to GIDs + // This is only useful for CID fonts. (Only // useful for OpenType CFF fonts.) - int *getCIDToGIDMap(int *nCIDs) const; + std::vector getCIDToGIDMap() const; // Returns the least restrictive embedding licensing right (as // defined by the TrueType spec): @@ -109,7 +112,7 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase // If is NULL, the encoding is unknown or undefined. The // array specifies the mapping from char codes to GIDs. // (Not useful for OpenType CFF fonts.) - void convertToType42(const char *psName, char **encoding, int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const; + void convertToType42(const char *psName, char **encoding, const std::vector &codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const; // Convert to a Type 1 font, suitable for embedding in a PostScript // file. This is only useful with 8-bit fonts. If is @@ -125,24 +128,24 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase // name (so we don't need to depend on the 'name' table in the // font). The array maps CIDs to GIDs; it has // entries. (Not useful for OpenType CFF fonts.) - void convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const; + void convertToCIDType2(const char *psName, const std::vector &cidMap, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const; // Convert to a Type 0 CIDFont, suitable for embedding in a // PostScript file. will be used as the PostScript font // name. (Only useful for OpenType CFF fonts.) - void convertToCIDType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const; + void convertToCIDType0(const char *psName, const std::vector &cidMap, FoFiOutputFunc outputFunc, void *outputStream) const; // Convert to a Type 0 (but non-CID) composite font, suitable for // embedding in a PostScript file. will be used as the // PostScript font name (so we don't need to depend on the 'name' // table in the font). The array maps CIDs to GIDs; it has // entries. (Not useful for OpenType CFF fonts.) - void convertToType0(const char *psName, int *cidMap, int nCIDs, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const; + void convertToType0(const char *psName, const std::vector &cidMap, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const; // Convert to a Type 0 (but non-CID) composite font, suitable for // embedding in a PostScript file. will be used as the // PostScript font name. (Only useful for OpenType CFF fonts.) - void convertToType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const; + void convertToType0(const char *psName, const std::vector &cidMap, FoFiOutputFunc outputFunc, void *outputStream) const; // Returns a pointer to the CFF font embedded in this OpenType font. // If successful, sets * and *, and returns true. @@ -158,10 +161,10 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase private: FoFiTrueType(const unsigned char *fileA, int lenA, bool freeFileDataA, int faceIndexA); void cvtEncoding(char **encoding, FoFiOutputFunc outputFunc, void *outputStream) const; - void cvtCharStrings(char **encoding, const int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const; + void cvtCharStrings(char **encoding, const std::vector &codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const; void cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const GooString *name, bool needVerticalMetrics, int *maxUsedGlyph) const; - void dumpString(const unsigned char *s, int length, FoFiOutputFunc outputFunc, void *outputStream) const; - unsigned int computeTableChecksum(const unsigned char *data, int length) const; + static void dumpString(std::span s, FoFiOutputFunc outputFunc, void *outputStream); + static unsigned int computeTableChecksum(std::span data); void parse(); void readPostTable(); int seekTable(const char *tag) const; @@ -171,10 +174,8 @@ class POPPLER_PRIVATE_EXPORT FoFiTrueType : public FoFiBase unsigned int scanLookupSubTable(unsigned int subTable, unsigned int orgGID); int checkGIDInCoverage(unsigned int coverage, unsigned int orgGID); - TrueTypeTable *tables; - int nTables; - TrueTypeCmap *cmaps; - int nCmaps; + std::vector tables; + std::vector cmaps; int nGlyphs; int locaFmt; int bbox[4]; diff --git a/fofi/FoFiType1.cc b/fofi/FoFiType1.cc index 3fcac6c..2cfa7d5 100644 --- a/fofi/FoFiType1.cc +++ b/fofi/FoFiType1.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005, 2008, 2010, 2018, 2021, 2022 Albert Astals Cid +// Copyright (C) 2005, 2008, 2010, 2018, 2021-2024 Albert Astals Cid // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2010 Jakub Wilk // Copyright (C) 2014 Carlos Garcia Campos @@ -28,6 +28,9 @@ #include +#include +#include + #include #include #include @@ -47,47 +50,24 @@ FoFiType1 *FoFiType1::make(const unsigned char *fileA, int lenA) return new FoFiType1(fileA, lenA, false); } -FoFiType1 *FoFiType1::load(const char *fileName) -{ - char *fileA; - int lenA; - - if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { - return nullptr; - } - return new FoFiType1((unsigned char *)fileA, lenA, true); -} - FoFiType1::FoFiType1(const unsigned char *fileA, int lenA, bool freeFileDataA) : FoFiBase(fileA, lenA, freeFileDataA) { - name = nullptr; encoding = nullptr; - fontMatrix[0] = 0.001; - fontMatrix[1] = 0; - fontMatrix[2] = 0; - fontMatrix[3] = 0.001; - fontMatrix[4] = 0; - fontMatrix[5] = 0; parsed = false; undoPFB(); } FoFiType1::~FoFiType1() { - int i; - - if (name) { - gfree(name); - } if (encoding && encoding != fofiType1StandardEncoding) { - for (i = 0; i < 256; ++i) { + for (int i = 0; i < 256; ++i) { gfree(encoding[i]); } - gfree(encoding); + gfree(static_cast(encoding)); } } -const char *FoFiType1::getName() +std::string FoFiType1::getName() { if (!parsed) { parse(); @@ -103,18 +83,6 @@ char **FoFiType1::getEncoding() return encoding; } -void FoFiType1::getFontMatrix(double *mat) -{ - int i; - - if (!parsed) { - parse(); - } - for (i = 0; i < 6; ++i) { - mat[i] = fontMatrix[i]; - } -} - void FoFiType1::writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc, void *outputStream) const { char buf[512]; @@ -122,7 +90,7 @@ void FoFiType1::writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc int i; // copy everything up to the encoding - for (line = (char *)file; line && strncmp(line, "/Encoding", 9); line = getNextLine(line)) { + for (line = (char *)file; line && (strncmp(line, "/Encoding", 9) != 0); line = getNextLine(line)) { ; } if (!line) { @@ -163,7 +131,7 @@ void FoFiType1::writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc // some fonts have two /Encoding entries in their dictionary, so we // check for a second one here if (line) { - for (line2 = line, i = 0; i < 20 && line2 && strncmp(line2, "/Encoding", 9); line2 = getNextLine(line2), ++i) { + for (line2 = line, i = 0; i < 20 && line2 && (strncmp(line2, "/Encoding", 9) != 0); line2 = getNextLine(line2), ++i) { ; } if (i < 20 && line2) { @@ -208,155 +176,132 @@ char *FoFiType1::getNextLine(char *line) const return line; } +static const char tokenSeparators[] = " \t\n\r"; + +class FoFiType1Tokenizer +{ +public: + explicit FoFiType1Tokenizer(std::string_view &&stringViewA) : stringView(stringViewA) { } + + std::optional getToken() + { + const auto length = stringView.length(); + if (currentPos >= length) { + return {}; + } + + std::string_view::size_type pos = stringView.find_first_of(tokenSeparators, currentPos); + while (pos == currentPos) { + // skip multiple contiguous separators + ++currentPos; + pos = stringView.find_first_of(tokenSeparators, currentPos); + } + if (pos == std::string_view::npos) { + const auto tokenLength = length - currentPos; + if (tokenLength > 0) { + std::string_view token = stringView.substr(currentPos, tokenLength); + currentPos = length; + return token; + } else { + currentPos = length; + return {}; + } + } + + std::string_view token = stringView.substr(currentPos, pos - currentPos); + + currentPos = pos + 1; + + return token; + } + +private: + std::string_view::size_type currentPos = 0; + const std::string_view stringView; +}; + void FoFiType1::parse() { - char *line, *line1, *firstLine, *p, *p2; - char buf[256]; - char c; - int n, code, base, i, j; - char *tokptr; - bool gotMatrix, continueLine; - - gotMatrix = false; - for (i = 1, line = (char *)file; i <= 100 && line && (!name || !encoding); ++i) { - - // get font name - if (!name && (line + 9 <= (char *)file + len) && !strncmp(line, "/FontName", 9)) { - const auto availableFile = (char *)file + len - line; - const int lineLen = static_cast(availableFile < 255 ? availableFile : 255); - strncpy(buf, line, lineLen); - buf[lineLen] = '\0'; - if ((p = strchr(buf + 9, '/')) && (p = strtok_r(p + 1, " \t\n\r", &tokptr))) { - name = copyString(p); + FoFiType1Tokenizer tokenizer(std::string_view(reinterpret_cast(file), len)); + while (name.empty() || !encoding) { + const std::optional token = tokenizer.getToken(); + + if (!token) { + break; + } + + if (name.empty() && token == "/FontName") { + const std::optional fontNameToken = tokenizer.getToken(); + if (!fontNameToken) { + break; } - line = getNextLine(line); - - // get encoding - } else if (!encoding && (line + 30 <= (char *)file + len) && !strncmp(line, "/Encoding StandardEncoding def", 30)) { - encoding = (char **)fofiType1StandardEncoding; - } else if (!encoding && (line + 19 <= (char *)file + len) && !strncmp(line, "/Encoding 256 array", 19)) { - encoding = (char **)gmallocn(256, sizeof(char *)); - for (j = 0; j < 256; ++j) { - encoding[j] = nullptr; + + // Skip the / + name = fontNameToken->substr(1); + + } else if (!encoding && token == "/Encoding") { + const std::optional token2 = tokenizer.getToken(); + if (!token2) { + break; } - continueLine = false; - for (j = 0, line = getNextLine(line); j < 300 && line && (line1 = getNextLine(line)); ++j, line = line1) { - if ((n = (int)(line1 - line)) > 255) { - error(errSyntaxWarning, -1, "FoFiType1::parse a line has more than 255 characters, we don't support this"); - n = 255; + + const std::optional token3 = tokenizer.getToken(); + if (!token3) { + break; + } + + if (token2 == "StandardEncoding" && token3 == "def") { + encoding = (char **)fofiType1StandardEncoding; + } else if (token2 == "256" && token3 == "array") { + encoding = (char **)gmallocn(256, sizeof(char *)); + for (int j = 0; j < 256; ++j) { + encoding[j] = nullptr; } - if (continueLine) { - continueLine = false; - if ((line1 - firstLine) + 1 > (int)sizeof(buf)) { + + while (true) { + const std::optional encodingToken = tokenizer.getToken(); + if (!encodingToken) { break; } - p = firstLine; - p2 = buf; - while (p < line1) { - if (*p == '\n' || *p == '\r') { - *p2++ = ' '; - p++; - } else { - *p2++ = *p++; - } - } - *p2 = '\0'; - } else { - firstLine = line; - strncpy(buf, line, n); - buf[n] = '\0'; - } - for (p = buf; *p == ' ' || *p == '\t'; ++p) { - ; - } - if (!strncmp(p, "dup", 3)) { - while (true) { - p += 3; - for (; *p == ' ' || *p == '\t'; ++p) { - ; - } - code = 0; - if (*p == '8' && p[1] == '#') { - base = 8; - p += 2; - } else if (*p >= '0' && *p <= '9') { - base = 10; - } else if (*p == '\n' || *p == '\r') { - continueLine = true; - break; - } else { + + if (encodingToken == "dup") { + std::optional codeToken = tokenizer.getToken(); + if (!codeToken) { break; } - for (; *p >= '0' && *p < '0' + base && code < INT_MAX / (base + (*p - '0')); ++p) { - code = code * base + (*p - '0'); - } - for (; *p == ' ' || *p == '\t'; ++p) { - ; + + std::optional nameToken; + // Sometimes font data has code and name together without spacing i.e. 33/exclam + // if that happens don't call getToken again and just split codeToken in 2 + const auto slashPositionInCodeToken = codeToken->find('/'); + if (slashPositionInCodeToken != std::string_view::npos) { + nameToken = codeToken->substr(slashPositionInCodeToken, codeToken->length() - slashPositionInCodeToken); + codeToken = codeToken->substr(0, slashPositionInCodeToken); + } else { + nameToken = tokenizer.getToken(); } - if (*p == '\n' || *p == '\r') { - continueLine = true; - break; - } else if (*p != '/') { + + if (!nameToken) { break; } - ++p; - for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) { - ; + + int code = 0; + if (codeToken->length() > 2 && codeToken->at(0) == '8' && codeToken->at(1) == '#') { + std::from_chars(codeToken->data() + 2, codeToken->data() + codeToken->length(), code, 8); + } else { + std::from_chars(codeToken->data(), codeToken->data() + codeToken->length(), code); } - if (code >= 0 && code < 256) { - c = *p2; - *p2 = '\0'; + + if (code >= 0 && code < 256 && nameToken->length() > 1) { gfree(encoding[code]); - encoding[code] = copyString(p); - *p2 = c; - } - for (p = p2; *p == ' ' || *p == '\t'; ++p) { - ; - } - if (*p == '\n' || *p == '\r') { - continueLine = true; - break; - } - if (strncmp(p, "put", 3)) { - break; - } - for (p += 3; *p == ' ' || *p == '\t'; ++p) { - ; - } - if (strncmp(p, "dup", 3)) { - break; + encoding[code] = copyString(nameToken->data() + 1, nameToken->length() - 1); } - } - } else { - if (strtok_r(buf, " \t", &tokptr) && (p = strtok_r(nullptr, " \t\n\r", &tokptr)) && !strcmp(p, "def")) { + + } else if (encodingToken == "def") { break; } } } - //~ check for getinterval/putinterval junk - - } else if (!gotMatrix && (line + 11 <= (char *)file + len) && !strncmp(line, "/FontMatrix", 11)) { - const auto availableFile = (char *)file + len - (line + 11); - const int bufLen = static_cast(availableFile < 255 ? availableFile : 255); - strncpy(buf, line + 11, bufLen); - buf[bufLen] = '\0'; - if ((p = strchr(buf, '['))) { - ++p; - if ((p2 = strchr(p, ']'))) { - *p2 = '\0'; - for (j = 0; j < 6; ++j) { - if ((p = strtok_r(j == 0 ? p : nullptr, " \t\n\r", &tokptr))) { - fontMatrix[j] = atof(p); - } else { - break; - } - } - } - } - gotMatrix = true; - - } else { - line = getNextLine(line); } } diff --git a/fofi/FoFiType1.h b/fofi/FoFiType1.h index 15e8ac0..b3cc91e 100644 --- a/fofi/FoFiType1.h +++ b/fofi/FoFiType1.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2018 Albert Astals Cid +// Copyright (C) 2018, 2022, 2023 Albert Astals Cid // Copyright (C) 2022 Oliver Sander // // To see a description of the changes please see the Changelog file that @@ -26,6 +26,8 @@ #include "FoFiBase.h" +#include + //------------------------------------------------------------------------ // FoFiType1 //------------------------------------------------------------------------ @@ -36,21 +38,15 @@ class FoFiType1 : public FoFiBase // Create a FoFiType1 object from a memory buffer. static FoFiType1 *make(const unsigned char *fileA, int lenA); - // Create a FoFiType1 object from a file on disk. - static FoFiType1 *load(const char *fileName); - ~FoFiType1() override; // Return the font name. - const char *getName(); + std::string getName(); // Return the encoding, as an array of 256 names (any of which may // be NULL). char **getEncoding(); - // Return the font matrix as an array of six numbers. - void getFontMatrix(double *mat); - // Write a version of the Type 1 font file with a new encoding. void writeEncoded(const char **newEncoding, FoFiOutputFunc outputFunc, void *outputStream) const; @@ -61,9 +57,8 @@ class FoFiType1 : public FoFiBase void parse(); void undoPFB(); - char *name; + std::string name; char **encoding; - double fontMatrix[6]; bool parsed; }; diff --git a/fofi/FoFiType1C.cc b/fofi/FoFiType1C.cc index 887bd28..5909e02 100644 --- a/fofi/FoFiType1C.cc +++ b/fofi/FoFiType1C.cc @@ -18,7 +18,8 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Tomoyuki Kubota // Copyright (C) 2019 Volker Krause -// Copyright (C) 2022 Oliver Sander +// Copyright (C) 2022, 2024 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -87,14 +88,12 @@ FoFiType1C::~FoFiType1C() { int i; - if (name) { - delete name; - } + delete name; if (encoding && encoding != fofiType1StandardEncoding && encoding != fofiType1ExpertEncoding) { for (i = 0; i < 256; ++i) { gfree(encoding[i]); } - gfree(encoding); + gfree(static_cast(encoding)); } if (privateDicts) { gfree(privateDicts); @@ -133,15 +132,13 @@ GooString *FoFiType1C::getGlyphName(int gid) const return new GooString(buf); } -int *FoFiType1C::getCIDToGIDMap(int *nCIDs) const +std::vector FoFiType1C::getCIDToGIDMap() const { - int *map; int n, i; // a CID font's top dict has ROS as the first operator if (topDict.firstOp != 0x0c1e) { - *nCIDs = 0; - return nullptr; + return {}; } // in a CID font, the charset data is the GID-to-CID mapping, so all @@ -153,12 +150,11 @@ int *FoFiType1C::getCIDToGIDMap(int *nCIDs) const } } ++n; - map = (int *)gmallocn(n, sizeof(int)); - memset(map, 0, n * sizeof(int)); + std::vector map; + map.resize(n, 0); for (i = 0; i < nGlyphs; ++i) { map[charset[i]] = i; } - *nCIDs = n; return map; } @@ -257,29 +253,29 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo } else { (*outputFunc)(outputStream, "/isFixedPitch false def\n", 24); } - std::unique_ptr buf = GooString::format("/ItalicAngle {0:.4g} def\n", topDict.italicAngle); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format("/ItalicAngle {0:.4g} def\n", topDict.italicAngle); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/UnderlinePosition {0:.4g} def\n", topDict.underlinePosition); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/UnderlineThickness {0:.4g} def\n", topDict.underlineThickness); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "end readonly def\n", 17); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, psNameLen); (*outputFunc)(outputStream, " def\n", 5); buf = GooString::format("/PaintType {0:d} def\n", topDict.paintType); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FontType 1 def\n", 16); buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] readonly def\n", topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2], topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] readonly def\n", topDict.fontBBox[0], topDict.fontBBox[1], topDict.fontBBox[2], topDict.fontBBox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); if (topDict.uniqueID != 0) { buf = GooString::format("/UniqueID {0:d} def\n", topDict.uniqueID); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } // write the encoding @@ -293,7 +289,7 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo for (i = 0; i < 256; ++i) { if (enc && enc[i]) { buf = GooString::format("dup {0:d} /{1:s} put\n", i, enc[i]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } } (*outputFunc)(outputStream, "readonly def\n", 13); @@ -322,7 +318,7 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo eexecWrite(&eb, "/BlueValues ["); for (i = 0; i < privateDicts[0].nBlueValues; ++i) { buf = GooString::format("{0:s}{1:d}", i > 0 ? " " : "", privateDicts[0].blueValues[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -330,7 +326,7 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo eexecWrite(&eb, "/OtherBlues ["); for (i = 0; i < privateDicts[0].nOtherBlues; ++i) { buf = GooString::format("{0:s}{1:d}", i > 0 ? " " : "", privateDicts[0].otherBlues[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -338,7 +334,7 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo eexecWrite(&eb, "/FamilyBlues ["); for (i = 0; i < privateDicts[0].nFamilyBlues; ++i) { buf = GooString::format("{0:s}{1:d}", i > 0 ? " " : "", privateDicts[0].familyBlues[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -346,35 +342,35 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo eexecWrite(&eb, "/FamilyOtherBlues ["); for (i = 0; i < privateDicts[0].nFamilyOtherBlues; ++i) { buf = GooString::format("{0:s}{1:d}", i > 0 ? " " : "", privateDicts[0].familyOtherBlues[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } if (privateDicts[0].blueScale != 0.039625) { buf = GooString::format("/BlueScale {0:.4g} def\n", privateDicts[0].blueScale); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].blueShift != 7) { buf = GooString::format("/BlueShift {0:d} def\n", privateDicts[0].blueShift); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[0].blueFuzz); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].hasStdHW) { buf = GooString::format("/StdHW [{0:.4g}] def\n", privateDicts[0].stdHW); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[0].stdVW); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].nStemSnapH) { eexecWrite(&eb, "/StemSnapH ["); for (i = 0; i < privateDicts[0].nStemSnapH; ++i) { buf = GooString::format("{0:s}{1:.4g}", i > 0 ? " " : "", privateDicts[0].stemSnapH[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -382,25 +378,25 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo eexecWrite(&eb, "/StemSnapV ["); for (i = 0; i < privateDicts[0].nStemSnapV; ++i) { buf = GooString::format("{0:s}{1:.4g}", i > 0 ? " " : "", privateDicts[0].stemSnapV[i]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } if (privateDicts[0].hasForceBold) { buf = GooString::format("/ForceBold {0:s} def\n", privateDicts[0].forceBold ? "true" : "false"); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].forceBoldThreshold != 0) { buf = GooString::format("/ForceBoldThreshold {0:.4g} def\n", privateDicts[0].forceBoldThreshold); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[0].languageGroup); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[0].expansionFactor != 0.06) { buf = GooString::format("/ExpansionFactor {0:.4g} def\n", privateDicts[0].expansionFactor); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } // set up subroutines @@ -412,7 +408,7 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo // write the CharStrings buf = GooString::format("2 index /CharStrings {0:d} dict dup begin\n", nGlyphs); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); for (i = 0; i < nGlyphs; ++i) { ok = true; getIndexVal(&charStringsIdx, i, &val, &ok); @@ -440,55 +436,51 @@ void FoFiType1C::convertToType1(const char *psName, const char **newEncoding, bo (*outputFunc)(outputStream, "cleartomark\n", 12); } -void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream) +void FoFiType1C::convertToCIDType0(const char *psName, const std::vector &codeMap, FoFiOutputFunc outputFunc, void *outputStream) { - int *cidMap; + std::vector cidMap; GooString *charStrings; int *charStringOffsets; Type1CIndex subrIdx; Type1CIndexVal val; - int nCIDs, gdBytes; + int gdBytes; char buf2[256]; bool ok; - int gid, offset, n, i, j, k; + int gid, offset, n, j, k; // compute the CID count and build the CID-to-GID mapping - if (codeMap) { - nCIDs = nCodes; - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCodes; ++i) { - if (codeMap[i] >= 0 && codeMap[i] < nGlyphs) { - cidMap[i] = codeMap[i]; + if (!codeMap.empty()) { + cidMap.reserve(codeMap.size()); + for (int c : codeMap) { + if (c >= 0 && c < nGlyphs) { + cidMap.push_back(c); } else { - cidMap[i] = -1; + cidMap.push_back(-1); } } } else if (topDict.firstOp == 0x0c1e) { - nCIDs = 0; - for (i = 0; i < nGlyphs && i < charsetLength; ++i) { + int nCIDs = 0; + for (int i = 0; i < nGlyphs && i < charsetLength; ++i) { if (charset[i] >= nCIDs) { nCIDs = charset[i] + 1; } } - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCIDs; ++i) { - cidMap[i] = -1; - } - for (i = 0; i < nGlyphs && i < charsetLength; ++i) { + cidMap.resize(nCIDs, -1); + for (int i = 0; i < nGlyphs && i < charsetLength; ++i) { cidMap[charset[i]] = i; } } else { - nCIDs = nGlyphs; - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCIDs; ++i) { + int nCIDs = nGlyphs; + cidMap.resize(nCIDs, 0); + for (int i = 0; i < nCIDs; ++i) { cidMap[i] = i; } } // build the charstrings charStrings = new GooString(); - charStringOffsets = (int *)gmallocn(nCIDs + 1, sizeof(int)); - for (i = 0; i < nCIDs; ++i) { + charStringOffsets = (int *)gmallocn(cidMap.size() + 1, sizeof(int)); + for (size_t i = 0; i < cidMap.size(); ++i) { charStringOffsets[i] = charStrings->getLength(); if ((gid = cidMap[i]) >= 0) { ok = true; @@ -503,13 +495,13 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n } } } - charStringOffsets[nCIDs] = charStrings->getLength(); + charStringOffsets[cidMap.size()] = charStrings->getLength(); // compute gdBytes = number of bytes needed for charstring offsets // (offset size needs to account for the charstring offset table, // with a worst case of five bytes per entry, plus the charstrings // themselves) - i = (nCIDs + 1) * 5 + charStrings->getLength(); + int i = (cidMap.size() + 1) * 5 + charStrings->getLength(); if (i < 0x100) { gdBytes = 1; } else if (i < 0x10000) { @@ -547,60 +539,60 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24); (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27); } - std::unique_ptr buf = GooString::format(" /Supplement {0:d} def\n", topDict.supplement); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format(" /Supplement {0:d} def\n", topDict.supplement); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "end def\n", 8); if (topDict.hasFontMatrix) { buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n", topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2], topDict.fontMatrix[3], topDict.fontMatrix[4], topDict.fontMatrix[5]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } else if (privateDicts[0].hasFontMatrix) { (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); } else { (*outputFunc)(outputStream, "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38); } buf = GooString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n", topDict.fontBBox[0], topDict.fontBBox[1], topDict.fontBBox[2], topDict.fontBBox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FontInfo 1 dict dup begin\n", 27); (*outputFunc)(outputStream, " /FSType 8 def\n", 16); (*outputFunc)(outputStream, "end def\n", 8); // CIDFont-specific entries - buf = GooString::format("/CIDCount {0:d} def\n", nCIDs); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + buf = GooString::format("/CIDCount {0:d} def\n", int(cidMap.size())); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FDBytes 1 def\n", 15); buf = GooString::format("/GDBytes {0:d} def\n", gdBytes); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/CIDMapOffset 0 def\n", 20); if (topDict.paintType != 0) { buf = GooString::format("/PaintType {0:d} def\n", topDict.paintType); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } // FDArray entry buf = GooString::format("/FDArray {0:d} array\n", nFDs); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); for (i = 0; i < nFDs; ++i) { buf = GooString::format("dup {0:d} 10 dict begin\n", i); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FontType 1 def\n", 16); if (privateDicts[i].hasFontMatrix) { buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n", privateDicts[i].fontMatrix[0], privateDicts[i].fontMatrix[1], privateDicts[i].fontMatrix[2], privateDicts[i].fontMatrix[3], privateDicts[i].fontMatrix[4], privateDicts[i].fontMatrix[5]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } else { (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); } buf = GooString::format("/PaintType {0:d} def\n", topDict.paintType); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/Private 32 dict begin\n", 23); if (privateDicts[i].nBlueValues) { (*outputFunc)(outputStream, "/BlueValues [", 13); for (j = 0; j < privateDicts[i].nBlueValues; ++j) { buf = GooString::format("{0:s}{1:d}", j > 0 ? " " : "", privateDicts[i].blueValues[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } @@ -608,7 +600,7 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, "/OtherBlues [", 13); for (j = 0; j < privateDicts[i].nOtherBlues; ++j) { buf = GooString::format("{0:s}{1:d}", j > 0 ? " " : "", privateDicts[i].otherBlues[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } @@ -616,7 +608,7 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, "/FamilyBlues [", 14); for (j = 0; j < privateDicts[i].nFamilyBlues; ++j) { buf = GooString::format("{0:s}{1:d}", j > 0 ? " " : "", privateDicts[i].familyBlues[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } @@ -624,35 +616,35 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, "/FamilyOtherBlues [", 19); for (j = 0; j < privateDicts[i].nFamilyOtherBlues; ++j) { buf = GooString::format("{0:s}{1:d}", j > 0 ? " " : "", privateDicts[i].familyOtherBlues[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } if (privateDicts[i].blueScale != 0.039625) { buf = GooString::format("/BlueScale {0:.4g} def\n", privateDicts[i].blueScale); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].blueShift != 7) { buf = GooString::format("/BlueShift {0:d} def\n", privateDicts[i].blueShift); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[i].blueFuzz); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].hasStdHW) { buf = GooString::format("/StdHW [{0:.4g}] def\n", privateDicts[i].stdHW); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[i].stdVW); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].nStemSnapH) { (*outputFunc)(outputStream, "/StemSnapH [", 12); for (j = 0; j < privateDicts[i].nStemSnapH; ++j) { buf = GooString::format("{0:s}{1:.4g}", j > 0 ? " " : "", privateDicts[i].stemSnapH[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } @@ -660,25 +652,25 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, "/StemSnapV [", 12); for (j = 0; j < privateDicts[i].nStemSnapV; ++j) { buf = GooString::format("{0:s}{1:.4g}", j > 0 ? " " : "", privateDicts[i].stemSnapV[j]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); } if (privateDicts[i].hasForceBold) { buf = GooString::format("/ForceBold {0:s} def\n", privateDicts[i].forceBold ? "true" : "false"); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].forceBoldThreshold != 0) { buf = GooString::format("/ForceBoldThreshold {0:.4g} def\n", privateDicts[i].forceBoldThreshold); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[i].languageGroup); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (privateDicts[i].expansionFactor != 0.06) { buf = GooString::format("/ExpansionFactor {0:.4g} def\n", privateDicts[i].expansionFactor); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "currentdict end def\n", 20); (*outputFunc)(outputStream, "currentdict end put\n", 20); @@ -686,14 +678,14 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n (*outputFunc)(outputStream, "def\n", 4); // start the binary section - offset = (nCIDs + 1) * (1 + gdBytes); + offset = (cidMap.size() + 1) * (1 + gdBytes); buf = GooString::format("(Hex) {0:d} StartData\n", offset + charStrings->getLength()); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); // write the charstring offset (CIDMap) table - for (i = 0; i <= nCIDs; i += 6) { - for (j = 0; j < 6 && i + j <= nCIDs; ++j) { - if (i + j < nCIDs && cidMap[i + j] >= 0 && fdSelect) { + for (i = 0; i <= int(cidMap.size()); i += 6) { + for (j = 0; j < 6 && i + j <= int(cidMap.size()); ++j) { + if (i + j < int(cidMap.size()) && cidMap[i + j] >= 0 && fdSelect) { buf2[0] = (char)fdSelect[cidMap[i + j]]; } else { buf2[0] = (char)0; @@ -705,7 +697,7 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n } for (k = 0; k <= gdBytes; ++k) { buf = GooString::format("{0:02x}", buf2[k] & 0xff); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } } (*outputFunc)(outputStream, "\n", 1); @@ -716,7 +708,7 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n for (i = 0; i < n; i += 32) { for (j = 0; j < 32 && i + j < n; ++j) { buf = GooString::format("{0:02x}", charStrings->getChar(i + j) & 0xff); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (i + 32 >= n) { (*outputFunc)(outputStream, ">", 1); @@ -726,55 +718,49 @@ void FoFiType1C::convertToCIDType0(const char *psName, const int *codeMap, int n gfree(charStringOffsets); delete charStrings; - gfree(cidMap); } -void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream) +void FoFiType1C::convertToType0(const char *psName, const std::vector &codeMap, FoFiOutputFunc outputFunc, void *outputStream) { - int *cidMap; + std::vector cidMap; Type1CIndex subrIdx; Type1CIndexVal val; - int nCIDs; Type1CEexecBuf eb; bool ok; - int fd, i, j, k; + int fd, j, k; // compute the CID count and build the CID-to-GID mapping - if (codeMap) { - nCIDs = nCodes; - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCodes; ++i) { - if (codeMap[i] >= 0 && codeMap[i] < nGlyphs) { - cidMap[i] = codeMap[i]; + if (!codeMap.empty()) { + cidMap.reserve(codeMap.size()); + for (int c : codeMap) { + if (c >= 0 && c < nGlyphs) { + cidMap.push_back(c); } else { - cidMap[i] = -1; + cidMap.push_back(-1); } } } else if (topDict.firstOp == 0x0c1e) { - nCIDs = 0; - for (i = 0; i < nGlyphs && i < charsetLength; ++i) { + int nCIDs = 0; + for (int i = 0; i < nGlyphs && i < charsetLength; ++i) { if (charset[i] >= nCIDs) { nCIDs = charset[i] + 1; } } - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCIDs; ++i) { - cidMap[i] = -1; - } - for (i = 0; i < nGlyphs && i < charsetLength; ++i) { + cidMap.resize(nCIDs, -1); + for (int i = 0; i < nGlyphs && i < charsetLength; ++i) { cidMap[charset[i]] = i; } } else { - nCIDs = nGlyphs; - cidMap = (int *)gmallocn(nCIDs, sizeof(int)); - for (i = 0; i < nCIDs; ++i) { + int nCIDs = nGlyphs; + cidMap.resize(nCIDs); + for (int i = 0; i < nCIDs; ++i) { cidMap[i] = i; } } if (privateDicts) { // write the descendant Type 1 fonts - for (i = 0; i < nCIDs; i += 256) { + for (int i = 0; i < int(cidMap.size()); i += 256) { //~ this assumes that all CIDs in this block have the same FD -- //~ to handle multiple FDs correctly, need to somehow divide the @@ -782,7 +768,7 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod fd = 0; // if fdSelect is NULL, we have an 8-bit font, so just leave fd=0 if (fdSelect) { - for (j = i == 0 ? 1 : 0; j < 256 && i + j < nCIDs; ++j) { + for (j = i == 0 ? 1 : 0; j < 256 && i + j < int(cidMap.size()); ++j) { if (cidMap[i + j] >= 0) { fd = fdSelect[cidMap[i + j]]; break; @@ -798,34 +784,34 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod (*outputFunc)(outputStream, "16 dict begin\n", 14); (*outputFunc)(outputStream, "/FontName /", 11); (*outputFunc)(outputStream, psName, strlen(psName)); - std::unique_ptr buf = GooString::format("_{0:02x} def\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + std::string buf = GooString::format("_{0:02x} def\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, "/FontType 1 def\n", 16); if (privateDicts[fd].hasFontMatrix) { buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n", privateDicts[fd].fontMatrix[0], privateDicts[fd].fontMatrix[1], privateDicts[fd].fontMatrix[2], privateDicts[fd].fontMatrix[3], privateDicts[fd].fontMatrix[4], privateDicts[fd].fontMatrix[5]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } else if (topDict.hasFontMatrix) { (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); } else { (*outputFunc)(outputStream, "/FontMatrix [0.001 0 0 0.001 0 0] def\n", 38); } buf = GooString::format("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] def\n", topDict.fontBBox[0], topDict.fontBBox[1], topDict.fontBBox[2], topDict.fontBBox[3]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); buf = GooString::format("/PaintType {0:d} def\n", topDict.paintType); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); if (topDict.paintType != 0) { buf = GooString::format("/StrokeWidth {0:.4g} def\n", topDict.strokeWidth); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); - for (j = 0; j < 256 && i + j < nCIDs; ++j) { + for (j = 0; j < 256 && i + j < int(cidMap.size()); ++j) { buf = GooString::format("dup {0:d} /c{1:02x} put\n", j, j); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } if (j < 256) { buf = GooString::format("{0:d} 1 255 {{ 1 index exch /.notdef put }} for\n", j); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "readonly def\n", 13); (*outputFunc)(outputStream, "currentdict end\n", 16); @@ -852,7 +838,7 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod eexecWrite(&eb, "/BlueValues ["); for (k = 0; k < privateDicts[fd].nBlueValues; ++k) { buf = GooString::format("{0:s}{1:d}", k > 0 ? " " : "", privateDicts[fd].blueValues[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -860,7 +846,7 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod eexecWrite(&eb, "/OtherBlues ["); for (k = 0; k < privateDicts[fd].nOtherBlues; ++k) { buf = GooString::format("{0:s}{1:d}", k > 0 ? " " : "", privateDicts[fd].otherBlues[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -868,7 +854,7 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod eexecWrite(&eb, "/FamilyBlues ["); for (k = 0; k < privateDicts[fd].nFamilyBlues; ++k) { buf = GooString::format("{0:s}{1:d}", k > 0 ? " " : "", privateDicts[fd].familyBlues[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -876,35 +862,35 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod eexecWrite(&eb, "/FamilyOtherBlues ["); for (k = 0; k < privateDicts[fd].nFamilyOtherBlues; ++k) { buf = GooString::format("{0:s}{1:d}", k > 0 ? " " : "", privateDicts[fd].familyOtherBlues[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } if (privateDicts[fd].blueScale != 0.039625) { buf = GooString::format("/BlueScale {0:.4g} def\n", privateDicts[fd].blueScale); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].blueShift != 7) { buf = GooString::format("/BlueShift {0:d} def\n", privateDicts[fd].blueShift); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].blueFuzz != 1) { buf = GooString::format("/BlueFuzz {0:d} def\n", privateDicts[fd].blueFuzz); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].hasStdHW) { buf = GooString::format("/StdHW [{0:.4g}] def\n", privateDicts[fd].stdHW); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].hasStdVW) { buf = GooString::format("/StdVW [{0:.4g}] def\n", privateDicts[fd].stdVW); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].nStemSnapH) { eexecWrite(&eb, "/StemSnapH ["); for (k = 0; k < privateDicts[fd].nStemSnapH; ++k) { buf = GooString::format("{0:s}{1:.4g}", k > 0 ? " " : "", privateDicts[fd].stemSnapH[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } @@ -912,25 +898,25 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod eexecWrite(&eb, "/StemSnapV ["); for (k = 0; k < privateDicts[fd].nStemSnapV; ++k) { buf = GooString::format("{0:s}{1:.4g}", k > 0 ? " " : "", privateDicts[fd].stemSnapV[k]); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } eexecWrite(&eb, "] def\n"); } if (privateDicts[fd].hasForceBold) { buf = GooString::format("/ForceBold {0:s} def\n", privateDicts[fd].forceBold ? "true" : "false"); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].forceBoldThreshold != 0) { buf = GooString::format("/ForceBoldThreshold {0:.4g} def\n", privateDicts[fd].forceBoldThreshold); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].languageGroup != 0) { buf = GooString::format("/LanguageGroup {0:d} def\n", privateDicts[fd].languageGroup); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } if (privateDicts[fd].expansionFactor != 0.06) { buf = GooString::format("/ExpansionFactor {0:.4g} def\n", privateDicts[fd].expansionFactor); - eexecWrite(&eb, buf->c_str()); + eexecWrite(&eb, buf.c_str()); } // set up the subroutines @@ -951,13 +937,13 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod } // write the CharStrings - for (j = 0; j < 256 && i + j < nCIDs; ++j) { + for (j = 0; j < 256 && i + j < int(cidMap.size()); ++j) { if (cidMap[i + j] >= 0) { ok = true; getIndexVal(&charStringsIdx, cidMap[i + j], &val, &ok); if (ok) { buf = GooString::format("c{0:02x}", j); - eexecCvtGlyph(&eb, buf->c_str(), val.pos, val.len, &subrIdx, &privateDicts[fd]); + eexecCvtGlyph(&eb, buf.c_str(), val.pos, val.len, &subrIdx, &privateDicts[fd]); } } } @@ -988,30 +974,28 @@ void FoFiType1C::convertToType0(const char *psName, const int *codeMap, int nCod (*outputFunc)(outputStream, " def\n", 5); (*outputFunc)(outputStream, "/FontType 0 def\n", 16); if (topDict.hasFontMatrix) { - const std::unique_ptr buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n", topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2], topDict.fontMatrix[3], - topDict.fontMatrix[4], topDict.fontMatrix[5]); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("/FontMatrix [{0:.8g} {1:.8g} {2:.8g} {3:.8g} {4:.8g} {5:.8g}] def\n", topDict.fontMatrix[0], topDict.fontMatrix[1], topDict.fontMatrix[2], topDict.fontMatrix[3], topDict.fontMatrix[4], + topDict.fontMatrix[5]); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } else { (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); } (*outputFunc)(outputStream, "/FMapType 2 def\n", 16); (*outputFunc)(outputStream, "/Encoding [\n", 12); - for (i = 0; i < nCIDs; i += 256) { - const std::unique_ptr buf = GooString::format("{0:d}\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + for (int i = 0; i < int(cidMap.size()); i += 256) { + const std::string buf = GooString::format("{0:d}\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); (*outputFunc)(outputStream, "/FDepVector [\n", 14); - for (i = 0; i < nCIDs; i += 256) { + for (int i = 0; i < int(cidMap.size()); i += 256) { (*outputFunc)(outputStream, "/", 1); (*outputFunc)(outputStream, psName, strlen(psName)); - const std::unique_ptr buf = GooString::format("_{0:02x} findfont\n", i >> 8); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::format("_{0:02x} findfont\n", i >> 8); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } (*outputFunc)(outputStream, "] def\n", 6); (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); - - gfree(cidMap); } void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName, int offset, int nBytes, const Type1CIndex *subrIdx, const Type1CPrivateDict *pDict) @@ -1023,8 +1007,8 @@ void FoFiType1C::eexecCvtGlyph(Type1CEexecBuf *eb, const char *glyphName, int of std::set offsetBeingParsed; cvtGlyph(offset, nBytes, charBuf, subrIdx, pDict, true, offsetBeingParsed); - const std::unique_ptr buf = GooString::format("/{0:s} {1:d} RD ", glyphName, charBuf->getLength()); - eexecWrite(eb, buf->c_str()); + const std::string buf = GooString::format("/{0:s} {1:d} RD ", glyphName, charBuf->getLength()); + eexecWrite(eb, buf.c_str()); eexecWriteCharstring(eb, (unsigned char *)charBuf->c_str(), charBuf->getLength()); eexecWrite(eb, " ND\n"); diff --git a/fofi/FoFiType1C.h b/fofi/FoFiType1C.h index f3a9b73..e296d0c 100644 --- a/fofi/FoFiType1C.h +++ b/fofi/FoFiType1C.h @@ -17,6 +17,7 @@ // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2018-2020 Albert Astals Cid // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -31,6 +32,7 @@ #include "poppler_private_export.h" #include +#include class GooString; @@ -175,7 +177,7 @@ class POPPLER_PRIVATE_EXPORT FoFiType1C : public FoFiBase // Return the mapping from CIDs to GIDs, and return the number of // CIDs in *. This is only useful for CID fonts. - int *getCIDToGIDMap(int *nCIDs) const; + std::vector getCIDToGIDMap() const; // Return the font matrix as an array of six numbers. void getFontMatrix(double *mat) const; @@ -196,7 +198,7 @@ class POPPLER_PRIVATE_EXPORT FoFiType1C : public FoFiBase // font's internal CID-to-GID mapping is used // (3) is is NULL and this is an 8-bit CFF font, then // the identity CID-to-GID mapping is used - void convertToCIDType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream); + void convertToCIDType0(const char *psName, const std::vector &codeMap, FoFiOutputFunc outputFunc, void *outputStream); // Convert to a Type 0 (but non-CID) composite font, suitable for // embedding in a PostScript file. will be used as the @@ -207,7 +209,7 @@ class POPPLER_PRIVATE_EXPORT FoFiType1C : public FoFiBase // font's internal CID-to-GID mapping is used // (3) is is NULL and this is an 8-bit CFF font, then // the identity CID-to-GID mapping is used - void convertToType0(const char *psName, const int *codeMap, int nCodes, FoFiOutputFunc outputFunc, void *outputStream); + void convertToType0(const char *psName, const std::vector &codeMap, FoFiOutputFunc outputFunc, void *outputStream); private: FoFiType1C(const unsigned char *fileA, int lenA, bool freeFileDataA); diff --git a/glib/CMakeLists.txt b/glib/CMakeLists.txt index e28d43a..24f6787 100644 --- a/glib/CMakeLists.txt +++ b/glib/CMakeLists.txt @@ -38,7 +38,7 @@ set(poppler_glib_public_headers poppler-structure-element.h ) -find_program(GLIB2_MKENUMS glib-mkenums) +find_program(GLIB2_MKENUMS glib-mkenums REQUIRED) find_program(GLIB2_MKENUMS_PYTHON NAMES python3 python) add_custom_command( @@ -90,20 +90,37 @@ set(poppler_glib_generated_SRCS ) add_library(poppler-glib ${poppler_glib_SRCS} ${poppler_glib_generated_SRCS}) generate_export_header(poppler-glib EXPORT_MACRO_NAME POPPLER_PUBLIC EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/poppler-macros.h") -set_target_properties(poppler-glib PROPERTIES VERSION 8.24.0 SOVERSION 8) +set_target_properties(poppler-glib PROPERTIES VERSION 8.29.0 SOVERSION 8) if(MINGW AND BUILD_SHARED_LIBS) get_target_property(POPPLER_GLIB_SOVERSION poppler-glib SOVERSION) set_target_properties(poppler-glib PROPERTIES SUFFIX "-${POPPLER_GLIB_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") endif() target_link_libraries(poppler-glib poppler PkgConfig::GLIB2 ${CAIRO_LIBRARIES} Freetype::Freetype) +target_include_directories(poppler-glib SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) install(TARGETS poppler-glib RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES +if (ENABLE_NSS3) + target_include_directories(poppler-glib SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) +endif() + +set(poppler_glib_all_install_headers ${poppler_glib_public_headers} ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-features.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-macros.h - DESTINATION include/poppler/glib) +) +if (${CMAKE_VERSION} VERSION_LESS "3.23.0") + install(FILES ${poppler_glib_all_install_headers} DESTINATION include/poppler/glib) +else() + target_sources(poppler-glib + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR} + FILES + ${poppler_glib_all_install_headers} + ) + install(TARGETS poppler-glib FILE_SET HEADERS DESTINATION include/poppler/glib) +endif() set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-enums.c" @@ -149,3 +166,5 @@ endif () if(ENABLE_GTK_DOC) add_subdirectory(reference) endif() + +check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) diff --git a/glib/demo/CMakeLists.txt b/glib/demo/CMakeLists.txt index 4c1ae9a..d77372a 100644 --- a/glib/demo/CMakeLists.txt +++ b/glib/demo/CMakeLists.txt @@ -18,7 +18,11 @@ set(poppler_glib_demo_SRCS layers.c selections.c taggedstruct.c + signature.c ) poppler_add_test(poppler-glib-demo BUILD_GTK_TESTS ${poppler_glib_demo_SRCS}) target_link_libraries(poppler-glib-demo ${CAIRO_LIBRARIES} poppler-glib PkgConfig::GTK3) +if (INSTALL_GLIB_DEMO) + install(TARGETS poppler-glib-demo DESTINATION bin) +endif () diff --git a/glib/demo/annots.c b/glib/demo/annots.c index 645a721..bfeb871 100644 --- a/glib/demo/annots.c +++ b/glib/demo/annots.c @@ -49,8 +49,8 @@ typedef struct } Annotations; static const Annotations supported_annots[] = { - { POPPLER_ANNOT_TEXT, "Text" }, { POPPLER_ANNOT_LINE, "Line" }, { POPPLER_ANNOT_SQUARE, "Square" }, { POPPLER_ANNOT_CIRCLE, "Circle" }, { POPPLER_ANNOT_HIGHLIGHT, "Highlight" }, - { POPPLER_ANNOT_UNDERLINE, "Underline" }, { POPPLER_ANNOT_SQUIGGLY, "Squiggly" }, { POPPLER_ANNOT_STRIKE_OUT, "Strike Out" }, { POPPLER_ANNOT_STAMP, "Stamp" }, + { POPPLER_ANNOT_TEXT, "Text" }, { POPPLER_ANNOT_FREE_TEXT, "Free Text" }, { POPPLER_ANNOT_LINE, "Line" }, { POPPLER_ANNOT_SQUARE, "Square" }, { POPPLER_ANNOT_CIRCLE, "Circle" }, + { POPPLER_ANNOT_HIGHLIGHT, "Highlight" }, { POPPLER_ANNOT_UNDERLINE, "Underline" }, { POPPLER_ANNOT_SQUIGGLY, "Squiggly" }, { POPPLER_ANNOT_STRIKE_OUT, "Strike Out" }, { POPPLER_ANNOT_STAMP, "Stamp" }, }; static const char *stamp_types[] = { [POPPLER_ANNOT_STAMP_ICON_UNKNOWN] = "Unknown", @@ -478,6 +478,27 @@ static void pgd_annot_view_set_annot_free_text(GtkWidget *table, PopplerAnnotFre text = get_free_text_callout_line(annot); pgd_table_add_property(GTK_GRID(table), "Callout:", text, row); g_free(text); + + PopplerColor *color = poppler_annot_get_color(POPPLER_ANNOT(annot)); + + if (color) { + text = g_strdup_printf("(%d, %d, %d)", color->red, color->green, color->blue); + } else { + text = g_strdup("null"); + } + pgd_table_add_property(GTK_GRID(table), "Color:", text, row); + g_free(text); + + PopplerFontDescription *font_desc = poppler_annot_free_text_get_font_desc(annot); + PangoFontDescription *p = pango_font_description_new(); + pango_font_description_set_family(p, font_desc->font_name); + pango_font_description_set_stretch(p, (PangoStretch)font_desc->stretch); + pango_font_description_set_weight(p, (PangoWeight)font_desc->weight); + pango_font_description_set_style(p, (PangoStyle)font_desc->style); + pango_font_description_set_size(p, font_desc->size_pt * PANGO_SCALE); + text = pango_font_description_to_string(p); + pgd_table_add_property(GTK_GRID(table), "Font:", text, row); + g_free(text); } static void pgd_annot_view_set_annot_stamp(GtkWidget *table, PopplerAnnotStamp *annot, gint *row) @@ -585,13 +606,8 @@ static void pgd_annot_view_set_annot(PgdAnnotsDemo *demo, PopplerAnnot *annot) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 8); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 8); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); @@ -616,6 +632,15 @@ static void pgd_annot_view_set_annot(PgdAnnotsDemo *demo, PopplerAnnot *annot) pgd_table_add_property(GTK_GRID(table), "Coords:", text, &row); g_free(text); + double border_width; + if (poppler_annot_get_border_width(POPPLER_ANNOT(annot), &border_width)) { + text = g_strdup_printf("%f", border_width); + } else { + text = g_strdup("unset"); + } + pgd_table_add_property(GTK_GRID(table), "Border Width:", text, &row); + g_free(text); + if (POPPLER_IS_ANNOT_MARKUP(annot)) { pgd_annot_view_set_annot_markup(table, POPPLER_ANNOT_MARKUP(annot), &row); } @@ -859,6 +884,17 @@ static void pgd_annots_add_annot(PgdAnnotsDemo *demo) case POPPLER_ANNOT_TEXT: annot = poppler_annot_text_new(demo->doc, &rect); + break; + case POPPLER_ANNOT_FREE_TEXT: + annot = poppler_annot_free_text_new(demo->doc, &rect); + poppler_annot_set_contents(annot, "Free Text"); + PopplerFontDescription *font_desc = poppler_font_description_new("DejaVu Sans"); + font_desc->size_pt = 17; + font_desc->style = POPPLER_STYLE_OBLIQUE; + font_desc->weight = POPPLER_WEIGHT_BOLD; + font_desc->stretch = POPPLER_STRETCH_CONDENSED; + poppler_annot_free_text_set_font_desc(POPPLER_ANNOT_FREE_TEXT(annot), font_desc); + poppler_font_description_free(font_desc); break; case POPPLER_ANNOT_LINE: { PopplerPoint start, end; @@ -1430,11 +1466,7 @@ GtkWidget *pgd_annots_create_widget(PopplerDocument *document) g_signal_connect(demo->darea, "button_release_event", G_CALLBACK(pgd_annots_drawing_area_button_release), (gpointer)demo); swindow = gtk_scrolled_window_new(NULL, NULL); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(swindow), demo->darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), demo->darea); -#endif gtk_widget_show(demo->darea); gtk_paned_add2(GTK_PANED(hpaned), swindow); diff --git a/glib/demo/find.c b/glib/demo/find.c index 1de91f2..79f91c0 100644 --- a/glib/demo/find.c +++ b/glib/demo/find.c @@ -460,11 +460,7 @@ GtkWidget *pgd_find_create_widget(PopplerDocument *document) g_signal_connect(demo->darea, "draw", G_CALLBACK(pgd_find_viewer_drawing_area_draw), demo); swindow = gtk_scrolled_window_new(NULL, NULL); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(swindow), demo->darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), demo->darea); -#endif gtk_widget_show(demo->darea); gtk_paned_add2(GTK_PANED(hpaned), swindow); diff --git a/glib/demo/fonts.c b/glib/demo/fonts.c index 00d80fb..85d28de 100644 --- a/glib/demo/fonts.c +++ b/glib/demo/fonts.c @@ -125,7 +125,8 @@ static gboolean pgd_fonts_fill_model(PgdFontsDemo *demo) font_info = poppler_font_info_new(demo->doc); - while (poppler_font_info_scan(font_info, 20, &fonts_iter)) { + while (scanned <= n_pages) { + poppler_font_info_scan(font_info, 20, &fonts_iter); pgd_fonts_update_progress(demo, n_pages, scanned); while (gtk_events_pending()) { diff --git a/glib/demo/forms.c b/glib/demo/forms.c index 05c331b..d53a599 100644 --- a/glib/demo/forms.c +++ b/glib/demo/forms.c @@ -144,13 +144,8 @@ static void pgd_form_field_view_set_field(GtkWidget *field_view, PopplerFormFiel table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); diff --git a/glib/demo/images.c b/glib/demo/images.c index 05ced60..7ece57e 100644 --- a/glib/demo/images.c +++ b/glib/demo/images.c @@ -89,11 +89,7 @@ static GtkWidget *pgd_image_view_new() darea = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(pgd_image_view_drawing_area_draw), (gpointer)swindow); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(swindow), darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), darea); -#endif gtk_widget_show(darea); return swindow; diff --git a/glib/demo/info.cc b/glib/demo/info.cc index 6f80757..5c2d121 100644 --- a/glib/demo/info.cc +++ b/glib/demo/info.cc @@ -168,13 +168,8 @@ GtkWidget *pgd_info_create_widget(PopplerDocument *document) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); diff --git a/glib/demo/layers.c b/glib/demo/layers.c index 24d63b7..3e1b71e 100644 --- a/glib/demo/layers.c +++ b/glib/demo/layers.c @@ -236,11 +236,7 @@ static GtkWidget *pgd_layers_create_viewer(PgdLayersDemo *demo) swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(swindow), demo->darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swindow), demo->darea); -#endif gtk_widget_show(demo->darea); gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0); @@ -261,7 +257,7 @@ static gboolean update_kids(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter return FALSE; } -static gboolean clear_rb_group(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *rb_group) +static gboolean clear_rb_group(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, const gint *rb_group) { gint group; diff --git a/glib/demo/main.c b/glib/demo/main.c index e7fdea1..311aa5e 100644 --- a/glib/demo/main.c +++ b/glib/demo/main.c @@ -38,6 +38,7 @@ #include "find.h" #include "print.h" #include "selections.h" +#include "signature.h" enum { @@ -69,7 +70,8 @@ static const PopplerGlibDemo demo_list[] = { { "Info", pgd_info_create_widget }, { "Text", pgd_text_create_widget }, { "Tagged Structure", pgd_taggedstruct_create_widget }, { "Find", pgd_find_create_widget }, - { "Print", pgd_print_create_widget } }; + { "Print", pgd_print_create_widget }, + { "Signature", pgd_signature_create_widget } }; static void pgd_demo_changed(GtkTreeSelection *selection, GtkNotebook *notebook) { @@ -187,12 +189,8 @@ static GtkDialog *pgd_demo_get_auth_dialog(GFile *uri_file) gtk_widget_show(main_vbox); label = gtk_label_new(NULL); -#if GTK_CHECK_VERSION(3, 15, 0) gtk_label_set_xalign(GTK_LABEL(label), 0.0); gtk_label_set_yalign(GTK_LABEL(label), 0.5); -#else - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); -#endif gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); file_name = g_file_get_basename(uri_file); format = g_strdup_printf("%s\n\n%s", "Password required", "The document “%s” is locked and requires a password before it can be opened."); @@ -216,12 +214,8 @@ static GtkDialog *pgd_demo_get_auth_dialog(GFile *uri_file) gtk_widget_show(table); label = gtk_label_new_with_mnemonic("_Password:"); -#if GTK_CHECK_VERSION(3, 15, 0) gtk_label_set_xalign(GTK_LABEL(label), 0.0); gtk_label_set_yalign(GTK_LABEL(label), 0.5); -#else - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); -#endif password_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(password_entry), FALSE); diff --git a/glib/demo/page.c b/glib/demo/page.c index f6a3d82..fe423bc 100644 --- a/glib/demo/page.c +++ b/glib/demo/page.c @@ -254,13 +254,8 @@ GtkWidget *pgd_page_create_widget(PopplerDocument *document) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); @@ -286,13 +281,8 @@ GtkWidget *pgd_page_create_widget(PopplerDocument *document) thumnail_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); gtk_widget_set_margin_top(thumnail_box, 5); gtk_widget_set_margin_bottom(thumnail_box, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(thumnail_box, 12); gtk_widget_set_margin_end(thumnail_box, 5); -#else - gtk_widget_set_margin_left(thumnail_box, 12); - gtk_widget_set_margin_right(thumnail_box, 5); -#endif demo->thumbnail = gtk_image_new(); gtk_box_pack_start(GTK_BOX(thumnail_box), demo->thumbnail, TRUE, TRUE, 0); diff --git a/glib/demo/print.c b/glib/demo/print.c index 7cf918f..509764c 100644 --- a/glib/demo/print.c +++ b/glib/demo/print.c @@ -64,7 +64,7 @@ static void pgd_print_draw_page(GtkPrintOperation *op, GtkPrintContext *context, GtkPrintSettings *settings; #endif PgdPrintOptions options; - PopplerPrintFlags flags = 0; + PopplerRenderAnnotsFlags flags = 0; page = poppler_document_get_page(demo->doc, page_nr); if (!page) { @@ -82,20 +82,20 @@ static void pgd_print_draw_page(GtkPrintOperation *op, GtkPrintContext *context, #endif switch (options) { case PRINT_DOCUMENT: - flags |= POPPLER_PRINT_DOCUMENT; + flags = POPPLER_RENDER_ANNOTS_PRINT_DOCUMENT; break; case PRINT_DOCUMENT_MARKUPS: - flags |= POPPLER_PRINT_MARKUP_ANNOTS; + flags = POPPLER_RENDER_ANNOTS_PRINT_MARKUP; break; case PRINT_DOCUMENT_STAMPS: - flags |= POPPLER_PRINT_STAMP_ANNOTS_ONLY; + flags = POPPLER_RENDER_ANNOTS_PRINT_STAMP; break; default: g_assert_not_reached(); } cr = gtk_print_context_get_cairo_context(context); - poppler_page_render_for_printing_with_options(page, cr, flags); + poppler_page_render_full(page, cr, TRUE, flags); g_object_unref(page); } diff --git a/glib/demo/render.c b/glib/demo/render.c index 12656aa..39e80ff 100644 --- a/glib/demo/render.c +++ b/glib/demo/render.c @@ -33,6 +33,7 @@ typedef struct gint rotate; GdkRectangle slice; gboolean printing; + gboolean highlight; GtkWidget *swindow; GtkWidget *darea; @@ -139,8 +140,10 @@ static void pgd_render_start(GtkButton *button, PgdRenderDemo *demo) if (demo->printing) { poppler_page_render_for_printing(page, cr); - } else { + } else if (demo->highlight) { poppler_page_render(page, cr); + } else { + poppler_page_render_full(page, cr, FALSE, POPPLER_RENDER_ANNOTS_ALL & (~POPPLER_RENDER_ANNOTS_HIGHLIGHT)); } cairo_restore(cr); @@ -209,6 +212,11 @@ static void pgd_render_printing_selector_changed(GtkToggleButton *tooglebutton, demo->printing = gtk_toggle_button_get_active(tooglebutton); } +static void pgd_render_highlight_selector_changed(GtkToggleButton *tooglebutton, PgdRenderDemo *demo) +{ + demo->highlight = gtk_toggle_button_get_active(tooglebutton); +} + static void pgd_render_slice_selector_value_changed(GtkSpinButton *spinbutton, PgdRenderDemo *demo) { demo->slice.x = (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(demo->slice_x)); @@ -224,7 +232,7 @@ GtkWidget *pgd_render_properties_selector_create(PgdRenderDemo *demo) GtkWidget *page_hbox, *page_selector; GtkWidget *scale_hbox, *scale_selector; GtkWidget *rotate_hbox, *rotate_selector; - GtkWidget *printing_selector; + GtkWidget *selector; GtkWidget *slice_hbox; GtkWidget *button; gint n_pages; @@ -292,10 +300,15 @@ GtkWidget *pgd_render_properties_selector_create(PgdRenderDemo *demo) gtk_box_pack_start(GTK_BOX(hbox), rotate_hbox, FALSE, TRUE, 0); gtk_widget_show(rotate_hbox); - printing_selector = gtk_check_button_new_with_label("Printing"); - g_signal_connect(printing_selector, "toggled", G_CALLBACK(pgd_render_printing_selector_changed), (gpointer)demo); - gtk_box_pack_start(GTK_BOX(hbox), printing_selector, FALSE, TRUE, 0); - gtk_widget_show(printing_selector); + selector = gtk_check_button_new_with_label("Printing"); + g_signal_connect(selector, "toggled", G_CALLBACK(pgd_render_printing_selector_changed), (gpointer)demo); + gtk_box_pack_start(GTK_BOX(hbox), selector, FALSE, TRUE, 0); + gtk_widget_show(selector); + + selector = gtk_check_button_new_with_label("Render highlight annots"); + g_signal_connect(selector, "toggled", G_CALLBACK(pgd_render_highlight_selector_changed), (gpointer)demo); + gtk_box_pack_start(GTK_BOX(hbox), selector, FALSE, TRUE, 0); + gtk_widget_show(selector); hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0); @@ -394,11 +407,7 @@ GtkWidget *pgd_render_create_widget(PopplerDocument *document) demo->swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(demo->swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(demo->swindow), demo->darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(demo->swindow), demo->darea); -#endif gtk_widget_show(demo->darea); gtk_box_pack_start(GTK_BOX(vbox), demo->swindow, TRUE, TRUE, 0); diff --git a/glib/demo/selections.c b/glib/demo/selections.c index a1196da..fe7c47b 100644 --- a/glib/demo/selections.c +++ b/glib/demo/selections.c @@ -585,11 +585,7 @@ GtkWidget *pgd_selections_create_widget(PopplerDocument *document) g_signal_connect(demo->darea, "query_tooltip", G_CALLBACK(pgd_selections_drawing_area_query_tooltip), (gpointer)demo); demo->swindow = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(demo->swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(demo->swindow), demo->darea); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(demo->swindow), demo->darea); -#endif gtk_widget_show(demo->darea); gtk_box_pack_start(GTK_BOX(vbox), demo->swindow, TRUE, TRUE, 0); diff --git a/glib/demo/signature.c b/glib/demo/signature.c new file mode 100644 index 0000000..e4e8a00 --- /dev/null +++ b/glib/demo/signature.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2022-2023 Jan-Michael Brummer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "signature.h" +#include "utils.h" + +typedef struct +{ + PopplerDocument *doc; + PopplerPage *page; + GtkWidget *darea; + cairo_surface_t *surface; + gint num_page; + gint redraw_idle; + GdkPoint start; + GdkPoint stop; + gboolean started; + GdkCursorType cursor; + GtkWidget *main_box; + gdouble scale; +} PgdSignatureDemo; + +/* Render area */ +static cairo_surface_t *pgd_signature_render_page(PgdSignatureDemo *demo) +{ + cairo_t *cr; + PopplerPage *page; + gdouble width, height; + cairo_surface_t *surface = NULL; + + page = poppler_document_get_page(demo->doc, demo->num_page); + if (!page) { + return NULL; + } + + poppler_page_get_size(page, &width, &height); + + width *= demo->scale; + height *= demo->scale; + gtk_widget_set_size_request(demo->darea, width, height); + + surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + cr = cairo_create(surface); + + if (demo->scale != 1.0) { + cairo_scale(cr, demo->scale, demo->scale); + } + + cairo_save(cr); + cairo_set_source_rgb(cr, 1, 1, 1); + + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); + cairo_restore(cr); + + cairo_save(cr); + poppler_page_render(page, cr); + cairo_restore(cr); + + cairo_destroy(cr); + g_object_unref(page); + + return surface; +} + +static void draw_selection_rect(PgdSignatureDemo *demo, cairo_t *cr) +{ + gint pos_x1, pos_x2, pos_y1, pos_y2; + gint x, y, w, h; + + pos_x1 = demo->start.x; + pos_y1 = demo->start.y; + pos_x2 = demo->stop.x; + pos_y2 = demo->stop.y; + + x = MIN(pos_x1, pos_x2); + y = MIN(pos_y1, pos_y2); + w = ABS(pos_x1 - pos_x2); + h = ABS(pos_y1 - pos_y2); + + if (w <= 0 || h <= 0) { + return; + } + + cairo_save(cr); + + cairo_rectangle(cr, x + 1, y + 1, w - 2, h - 2); + cairo_set_source_rgba(cr, 0.2, 0.6, 0.8, 0.2); + cairo_fill(cr); + + cairo_rectangle(cr, x + 0.5, y + 0.5, w - 1, h - 1); + cairo_set_source_rgba(cr, 0.2, 0.6, 0.8, 0.35); + cairo_set_line_width(cr, 1); + cairo_stroke(cr); + + cairo_restore(cr); +} + +static gboolean pgd_signature_view_drawing_area_draw(GtkWidget *area, cairo_t *cr, PgdSignatureDemo *demo) +{ + if (demo->num_page == -1) { + return FALSE; + } + + if (!demo->surface) { + demo->surface = pgd_signature_render_page(demo); + if (!demo->surface) { + return FALSE; + } + } + + cairo_set_source_surface(cr, demo->surface, 0, 0); + cairo_paint(cr); + + if (demo->started) { + draw_selection_rect(demo, cr); + } + + return TRUE; +} + +static gboolean pgd_signature_viewer_redraw(PgdSignatureDemo *demo) +{ + cairo_surface_destroy(demo->surface); + demo->surface = NULL; + + gtk_widget_queue_draw(demo->darea); + + demo->redraw_idle = 0; + + return FALSE; +} + +static void pgd_signature_viewer_queue_redraw(PgdSignatureDemo *demo) +{ + if (demo->redraw_idle == 0) { + demo->redraw_idle = g_idle_add((GSourceFunc)pgd_signature_viewer_redraw, demo); + } +} + +static void pgd_signature_page_selector_value_changed(GtkSpinButton *spinbutton, PgdSignatureDemo *demo) +{ + demo->num_page = (gint)gtk_spin_button_get_value(spinbutton) - 1; + pgd_signature_viewer_queue_redraw(demo); + + demo->page = poppler_document_get_page(demo->doc, demo->num_page); +} + +static void pgd_signature_drawing_area_realize(GtkWidget *area, PgdSignatureDemo *demo) +{ + gtk_widget_add_events(area, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); +} + +char *password_callback(const char *in) +{ + GtkWidget *dialog; + GtkWidget *box; + GtkWidget *entry; + char *ret; + + dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, "Enter password"); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "Enter password to open: %s", in); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + + box = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog)); + entry = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_box_pack_end(GTK_BOX(box), entry, TRUE, TRUE, 6); + gtk_widget_show_all(box); + + gtk_dialog_run(GTK_DIALOG(dialog)); + ret = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + gtk_widget_destroy(dialog); + + return ret; +} + +static void pgd_signature_update_cursor(PgdSignatureDemo *demo, GdkCursorType cursor_type) +{ + GdkCursor *cursor = NULL; + + if (cursor_type == demo->cursor) { + return; + } + + if (cursor_type != GDK_LAST_CURSOR) { + cursor = gdk_cursor_new_for_display(gtk_widget_get_display(demo->main_box), cursor_type); + } + + demo->cursor = cursor_type; + + gdk_window_set_cursor(gtk_widget_get_window(demo->main_box), cursor); + gdk_display_flush(gtk_widget_get_display(demo->main_box)); + if (cursor) { + g_object_unref(cursor); + } +} + +static void pgd_signature_start_signing(GtkWidget *button, PgdSignatureDemo *demo) +{ + demo->start.x = 0; + demo->start.y = 0; + demo->stop.x = 0; + demo->stop.y = 0; + demo->started = TRUE; + pgd_signature_update_cursor(demo, GDK_TCROSS); +} + +static gboolean pgd_signature_drawing_area_button_press(GtkWidget *area, GdkEventButton *event, PgdSignatureDemo *demo) +{ + if (!demo->page || event->button != 1 || !demo->started) { + return FALSE; + } + + demo->start.x = event->x; + demo->start.y = event->y; + demo->stop = demo->start; + + pgd_signature_viewer_queue_redraw(demo); + + return TRUE; +} + +static gboolean pgd_signature_drawing_area_motion_notify(GtkWidget *area, GdkEventMotion *event, PgdSignatureDemo *demo) +{ + gdouble width, height; + + if (!demo->page || demo->start.x == -1 || !demo->started) { + return FALSE; + } + + demo->stop.x = event->x; + demo->stop.y = event->y; + + poppler_page_get_size(demo->page, &width, &height); + width *= demo->scale; + height *= demo->scale; + + /* Keep the drawing within the page */ + demo->stop.x = CLAMP(demo->stop.x, 0, width); + demo->stop.y = CLAMP(demo->stop.y, 0, height); + + pgd_signature_viewer_queue_redraw(demo); + + return TRUE; +} + +static void on_signing_done(GObject *source, GAsyncResult *result, gpointer user_data) +{ + PopplerDocument *document = POPPLER_DOCUMENT(source); + GError *error = NULL; + gboolean ret = poppler_document_sign_finish(document, result, &error); + + g_print("%s: result %d\n", __FUNCTION__, ret); + if (error) { + g_print("Error: %s", error->message); + g_error_free(error); + } +} + +static gboolean pgd_signature_drawing_area_button_release(GtkWidget *area, GdkEventButton *event, PgdSignatureDemo *demo) +{ + if (!demo->page || event->button != 1 || !demo->started) { + return FALSE; + } + + demo->started = FALSE; + pgd_signature_update_cursor(demo, GDK_LAST_CURSOR); + + /* poppler_certificate_set_nss_dir ("./glib/demo/cert"); */ + poppler_set_nss_password_callback(password_callback); + GList *available_certificates = poppler_get_available_signing_certificates(); + + if (available_certificates) { + char *signature; + char *signature_left; + PopplerSigningData *data = poppler_signing_data_new(); + PopplerRectangle rect; + PopplerCertificateInfo *certificate_info; + time_t t; + double width, height; + + certificate_info = available_certificates->data; + time(&t); + + poppler_signing_data_set_certificate_info(data, certificate_info); + poppler_signing_data_set_page(data, demo->num_page); + poppler_signing_data_set_field_partial_name(data, g_uuid_string_random()); + poppler_signing_data_set_destination_filename(data, "test.pdf"); + poppler_signing_data_set_reason(data, "I'm the author"); + poppler_signing_data_set_location(data, "At my desk"); + + poppler_page_get_size(demo->page, &width, &height); + + rect.x1 = demo->start.x > demo->stop.x ? demo->stop.x : demo->start.x; + rect.y1 = demo->start.y > demo->stop.y ? demo->stop.y : demo->start.y; + rect.x2 = demo->start.x > demo->stop.x ? demo->start.x : demo->stop.x; + rect.y2 = demo->start.y > demo->stop.y ? demo->start.y : demo->stop.y; + + /* Adjust scale */ + rect.x1 /= demo->scale; + rect.y1 /= demo->scale; + rect.x2 /= demo->scale; + rect.y2 /= demo->scale; + + rect.y1 = height - rect.y1; + rect.y2 = height - rect.y2; + + poppler_signing_data_set_signature_rectangle(data, &rect); + + signature = g_strdup_printf("Digitally signed by %s\nDate: %s", poppler_certificate_info_get_subject_common_name(certificate_info), ctime(&t)); + poppler_signing_data_set_signature_text(data, signature); + g_free(signature); + + signature_left = g_strdup_printf("%s", poppler_certificate_info_get_subject_common_name(certificate_info)); + poppler_signing_data_set_signature_text_left(data, signature_left); + g_free(signature_left); + + poppler_document_sign(demo->doc, data, NULL, on_signing_done, NULL); + } + + return TRUE; +} + +static void pgd_signature_scale_selector_value_changed(GtkSpinButton *spinbutton, PgdSignatureDemo *demo) +{ + demo->scale = gtk_spin_button_get_value(spinbutton); + pgd_signature_viewer_queue_redraw(demo); +} + +/* Main UI */ +GtkWidget *pgd_signature_create_widget(PopplerDocument *document) +{ + PgdSignatureDemo *demo; + GtkWidget *label; + GtkWidget *vbox; + GtkWidget *button; + GtkWidget *hbox, *page_selector; + GtkWidget *scale_hbox, *scale_selector; + GtkWidget *swindow; + gchar *str; + gint n_pages; + + demo = g_new0(PgdSignatureDemo, 1); + demo->cursor = GDK_LAST_CURSOR; + demo->doc = g_object_ref(document); + demo->scale = 1.0; + + n_pages = poppler_document_get_n_pages(document); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + + label = gtk_label_new("Page:"); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); + gtk_widget_show(label); + + page_selector = gtk_spin_button_new_with_range(1, n_pages, 1); + g_signal_connect(G_OBJECT(page_selector), "value-changed", G_CALLBACK(pgd_signature_page_selector_value_changed), (gpointer)demo); + gtk_box_pack_start(GTK_BOX(hbox), page_selector, FALSE, TRUE, 0); + gtk_widget_show(page_selector); + + str = g_strdup_printf("of %d", n_pages); + label = gtk_label_new(str); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0); + gtk_widget_show(label); + g_free(str); + + scale_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + + label = gtk_label_new("Scale:"); + gtk_box_pack_start(GTK_BOX(scale_hbox), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + scale_selector = gtk_spin_button_new_with_range(0, 10.0, 0.1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(scale_selector), 1.0); + g_signal_connect(G_OBJECT(scale_selector), "value-changed", G_CALLBACK(pgd_signature_scale_selector_value_changed), (gpointer)demo); + gtk_box_pack_start(GTK_BOX(scale_hbox), scale_selector, TRUE, TRUE, 0); + gtk_widget_show(scale_selector); + + gtk_box_pack_start(GTK_BOX(hbox), scale_hbox, FALSE, TRUE, 0); + gtk_widget_show(scale_hbox); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + button = gtk_button_new_with_mnemonic("_Sign"); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(pgd_signature_start_signing), (gpointer)demo); + gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0); + gtk_widget_show(button); + + gtk_widget_show(hbox); + + /* Demo Area (Render) */ + demo->darea = gtk_drawing_area_new(); + g_signal_connect(demo->darea, "draw", G_CALLBACK(pgd_signature_view_drawing_area_draw), demo); + g_signal_connect(demo->darea, "realize", G_CALLBACK(pgd_signature_drawing_area_realize), (gpointer)demo); + g_signal_connect(demo->darea, "button_press_event", G_CALLBACK(pgd_signature_drawing_area_button_press), (gpointer)demo); + g_signal_connect(demo->darea, "motion_notify_event", G_CALLBACK(pgd_signature_drawing_area_motion_notify), (gpointer)demo); + g_signal_connect(demo->darea, "button_release_event", G_CALLBACK(pgd_signature_drawing_area_button_release), (gpointer)demo); + + swindow = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(swindow), demo->darea); + gtk_widget_show(demo->darea); + + gtk_widget_show(swindow); + + gtk_box_pack_start(GTK_BOX(vbox), swindow, TRUE, TRUE, 0); + + demo->main_box = vbox; + + demo->num_page = 0; + demo->page = poppler_document_get_page(demo->doc, demo->num_page); + pgd_signature_viewer_queue_redraw(demo); + + return vbox; +} diff --git a/glib/demo/signature.h b/glib/demo/signature.h new file mode 100644 index 0000000..cbdf7f4 --- /dev/null +++ b/glib/demo/signature.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 Jan-Michael Brummer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#ifndef _SIGNATURE_H_ +# define _SIGNATURE_H_ + +G_BEGIN_DECLS + +GtkWidget *pgd_signature_create_widget(PopplerDocument *document); + +G_END_DECLS + +#endif /* _SIGNATURE_H_ */ diff --git a/glib/demo/text.c b/glib/demo/text.c index 4b1669d..85d47f8 100644 --- a/glib/demo/text.c +++ b/glib/demo/text.c @@ -447,13 +447,8 @@ GtkWidget *pgd_text_create_widget(PopplerDocument *document) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); diff --git a/glib/demo/utils.c b/glib/demo/utils.c index e41b742..85a4169 100644 --- a/glib/demo/utils.c +++ b/glib/demo/utils.c @@ -138,13 +138,8 @@ static void pgd_action_view_add_destination(GtkWidget *action_view, GtkGrid *tab new_table = gtk_grid_new(); gtk_widget_set_margin_top(new_table, 5); gtk_widget_set_margin_bottom(new_table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(new_table, 12); gtk_widget_set_margin_end(new_table, 5); -#else - gtk_widget_set_margin_left(new_table, 12); - gtk_widget_set_margin_right(new_table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(new_table), 6); gtk_grid_set_row_spacing(GTK_GRID(new_table), 6); @@ -237,14 +232,10 @@ static void pgd_action_view_play_rendition(GtkWidget *button, PopplerMedia *medi uri = g_file_get_uri(file); g_object_unref(file); if (uri) { -#if GTK_CHECK_VERSION(3, 22, 0) GtkWidget *toplevel; toplevel = gtk_widget_get_toplevel(button); gtk_show_uri_on_window(gtk_widget_is_toplevel(toplevel) ? GTK_WINDOW(toplevel) : NULL, uri, GDK_CURRENT_TIME, NULL); -#else - gtk_show_uri(gtk_widget_get_screen(button), uri, GDK_CURRENT_TIME, NULL); -#endif g_free(uri); } } @@ -296,13 +287,8 @@ void pgd_action_view_set_action(GtkWidget *action_view, PopplerAction *action) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); @@ -494,14 +480,10 @@ static void pgd_movie_view_play_movie(GtkWidget *button, PopplerMovie *movie) uri = g_file_get_uri(file); g_object_unref(file); if (uri) { -#if GTK_CHECK_VERSION(3, 22, 0) GtkWidget *toplevel; toplevel = gtk_widget_get_toplevel(button); gtk_show_uri_on_window(gtk_widget_is_toplevel(toplevel) ? GTK_WINDOW(toplevel) : NULL, uri, GDK_CURRENT_TIME, NULL); -#else - gtk_show_uri(gtk_widget_get_screen(button), uri, GDK_CURRENT_TIME, NULL); -#endif g_free(uri); } } @@ -526,13 +508,8 @@ void pgd_movie_view_set_movie(GtkWidget *movie_view, PopplerMovie *movie) table = gtk_grid_new(); gtk_widget_set_margin_top(table, 5); gtk_widget_set_margin_bottom(table, 5); -#if GTK_CHECK_VERSION(3, 12, 0) gtk_widget_set_margin_start(table, 12); gtk_widget_set_margin_end(table, 5); -#else - gtk_widget_set_margin_left(table, 12); - gtk_widget_set_margin_right(table, 5); -#endif gtk_grid_set_column_spacing(GTK_GRID(table), 6); gtk_grid_set_row_spacing(GTK_GRID(table), 6); diff --git a/glib/poppler-action.cc b/glib/poppler-action.cc index 726daed..cac3189 100644 --- a/glib/poppler-action.cc +++ b/glib/poppler-action.cc @@ -408,7 +408,7 @@ static void build_goto_remote(PopplerAction *action, const LinkGoToR *link) static void build_launch(PopplerAction *action, const LinkLaunch *link) { if (link->getFileName()) { - action->launch.file_name = g_strdup(link->getFileName()->c_str()); + action->launch.file_name = _poppler_goo_string_to_utf8(link->getFileName()); } if (link->getParams()) { action->launch.params = g_strdup(link->getParams()->c_str()); diff --git a/glib/poppler-action.h b/glib/poppler-action.h index b4ea01e..a788735 100644 --- a/glib/poppler-action.h +++ b/glib/poppler-action.h @@ -175,6 +175,8 @@ typedef struct _PopplerActionResetForm PopplerActionResetForm; * with poppler_named_dest_to_bytestring() first. * Also note that @named_dest does not have a defined encoding and * is not in a form suitable to be displayed to the user. + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerDest { @@ -407,6 +409,8 @@ struct _PopplerActionResetForm * PopplerAction: * * A generic wrapper for actions that exposes only #PopplerActionType. + * + * Since 24.10 this type supports g_autoptr */ union _PopplerAction { PopplerActionType type; @@ -451,4 +455,7 @@ guint8 *poppler_named_dest_to_bytestring(const char *name, gsize *length); G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerAction, poppler_action_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerDest, poppler_dest_free) + #endif /* __POPPLER_GLIB_H__ */ diff --git a/glib/poppler-annot.cc b/glib/poppler-annot.cc index b995f03..aaa31b9 100644 --- a/glib/poppler-annot.cc +++ b/glib/poppler-annot.cc @@ -3,6 +3,8 @@ * Copyright (C) 2007 Inigo Martinez * Copyright (C) 2009 Carlos Garcia Campos * Copyright (C) 2013 German Poo-Caamano + * Copyright (C) 2025 Albert Astals Cid + * Copyright (C) 2025 Markus Göllnitz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +25,7 @@ #include "poppler.h" #include "poppler-private.h" -#define ZERO_CROPBOX(c) (!(c && (c->x1 > 0.01 || c->y1 > 0.01))) +#define ZERO_CROPBOX(c) (!((c) && ((c)->x1 > 0.01 || (c)->y1 > 0.01))) const PDFRectangle *_poppler_annot_get_cropbox_and_page(PopplerAnnot *poppler_annot, Page **page_out); AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(cairo_surface_t *image, PDFDoc *doc, GError **error); @@ -85,6 +87,8 @@ struct _PopplerAnnotTextMarkupClass struct _PopplerAnnotFreeText { PopplerAnnotMarkup parent_instance; + PopplerFontDescription *font_desc; + PopplerColor font_color; }; struct _PopplerAnnotFreeTextClass @@ -157,11 +161,11 @@ struct _PopplerAnnotSquareClass }; struct _PopplerAnnotStamp { - PopplerAnnot parent_instance; + PopplerAnnotMarkup parent_instance; }; struct _PopplerAnnotStampClass { - PopplerAnnotClass parent_class; + PopplerAnnotMarkupClass parent_class; }; G_DEFINE_TYPE(PopplerAnnot, poppler_annot, G_TYPE_OBJECT) @@ -175,7 +179,7 @@ G_DEFINE_TYPE(PopplerAnnotScreen, poppler_annot_screen, POPPLER_TYPE_ANNOT) G_DEFINE_TYPE(PopplerAnnotLine, poppler_annot_line, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotCircle, poppler_annot_circle, POPPLER_TYPE_ANNOT_MARKUP) G_DEFINE_TYPE(PopplerAnnotSquare, poppler_annot_square, POPPLER_TYPE_ANNOT_MARKUP) -G_DEFINE_TYPE(PopplerAnnotStamp, poppler_annot_stamp, POPPLER_TYPE_ANNOT) +G_DEFINE_TYPE(PopplerAnnotStamp, poppler_annot_stamp, POPPLER_TYPE_ANNOT_MARKUP) static PopplerAnnot *_poppler_create_annot(GType annot_type, Annot *annot) { @@ -420,13 +424,205 @@ PopplerAnnot *poppler_annot_text_markup_new_underline(PopplerDocument *doc, Popp return poppler_annot; } +PopplerColor *_poppler_convert_annot_color_to_poppler_color(const AnnotColor *color) +{ + PopplerColor *poppler_color = nullptr; + + if (color) { + const double *values = color->getValues(); + + switch (color->getSpace()) { + case AnnotColor::colorGray: + poppler_color = g_new(PopplerColor, 1); + + poppler_color->red = CLAMP((guint16)(values[0] * 65535), 0, 65535); + poppler_color->green = poppler_color->red; + poppler_color->blue = poppler_color->red; + + break; + case AnnotColor::colorRGB: + poppler_color = g_new(PopplerColor, 1); + + poppler_color->red = CLAMP((guint16)(values[0] * 65535), 0, 65535); + poppler_color->green = CLAMP((guint16)(values[1] * 65535), 0, 65535); + poppler_color->blue = CLAMP((guint16)(values[2] * 65535), 0, 65535); + + break; + case AnnotColor::colorCMYK: + g_warning("Unsupported Annot Color: colorCMYK"); + break; + case AnnotColor::colorTransparent: + break; + } + } + + return poppler_color; +} + +std::unique_ptr _poppler_convert_poppler_color_to_annot_color(const PopplerColor *poppler_color) +{ + if (!poppler_color) { + return nullptr; + } + + return std::make_unique(CLAMP((double)poppler_color->red / 65535, 0., 1.), CLAMP((double)poppler_color->green / 65535, 0., 1.), CLAMP((double)poppler_color->blue / 65535, 0., 1.)); +} + static void poppler_annot_free_text_init(PopplerAnnotFreeText *poppler_annot) { } static void poppler_annot_free_text_class_init(PopplerAnnotFreeTextClass *klass) { } +// Single map with string keys and enum values +enum FontPropType +{ + TYPE_STYLE, + TYPE_WEIGHT, + TYPE_STRETCH, + TYPE_NORMAL +}; + +typedef std::map> FontstyleMap; + +static const FontstyleMap string_to_fontstyle = { { "UltraCondensed", std::pair(TYPE_STRETCH, POPPLER_STRETCH_ULTRA_CONDENSED) }, + { "ExtraCondensed", std::pair(TYPE_STRETCH, POPPLER_STRETCH_EXTRA_CONDENSED) }, + { "Condensed", std::pair(TYPE_STRETCH, POPPLER_STRETCH_CONDENSED) }, + { "SemiCondensed", std::pair(TYPE_STRETCH, POPPLER_STRETCH_SEMI_CONDENSED) }, + { "SemiExpanded", std::pair(TYPE_STRETCH, POPPLER_STRETCH_SEMI_EXPANDED) }, + { "Expanded", std::pair(TYPE_STRETCH, POPPLER_STRETCH_EXPANDED) }, + { "UltraExpanded", std::pair(TYPE_STRETCH, POPPLER_STRETCH_ULTRA_EXPANDED) }, + { "ExtraExpanded", std::pair(TYPE_STRETCH, POPPLER_STRETCH_EXTRA_EXPANDED) }, + { "Thin", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_THIN) }, + { "UltraLight", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_ULTRALIGHT) }, + { "Light", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_LIGHT) }, + { "Medium", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_MEDIUM) }, + { "SemiBold", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_SEMIBOLD) }, + { "Bold", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_BOLD) }, + { "UltraBold", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_ULTRABOLD) }, + { "Heavy", std::pair(TYPE_WEIGHT, POPPLER_WEIGHT_HEAVY) }, + { "Italic", std::pair(TYPE_STYLE, POPPLER_STYLE_ITALIC) }, + { "Oblique", std::pair(TYPE_STYLE, POPPLER_STYLE_OBLIQUE) }, + { "Regular", std::pair(TYPE_NORMAL, 0) }, + { "Normal", std::pair(TYPE_NORMAL, 0) } }; + +static const char *stretch_to_str[] = { "UltraCondensed", "ExtraCondensed", "Condensed", "SemiCondensed", /* Normal */ "", "SemiExpanded", "Expanded", "ExtraExpanded", "UltraExpanded" }; + +const std::map fallback_fonts = { + { "/Helvetica", "Helvetica" }, /* iOS */ + { "Helv", "Helvetica" } /* Firefox */ +}; + +static bool update_font_desc_with_word(PopplerFontDescription &font_desc, std::string &word) +{ + FontstyleMap::const_iterator a = string_to_fontstyle.find(word); + if (a != string_to_fontstyle.end()) { + std::pair elt = a->second; + switch (elt.first) { + case TYPE_STYLE: + font_desc.style = (PopplerStyle)elt.second; + return true; + case TYPE_WEIGHT: + font_desc.weight = (PopplerWeight)elt.second; + return true; + case TYPE_STRETCH: + font_desc.stretch = (PopplerStretch)elt.second; + return true; + case TYPE_NORMAL: + return true; + } + } + return false; +} + +static void poppler_font_name_to_description(const std::string &name, PopplerFontDescription &font_desc) +{ + /* Last three words of the font name may be style indications */ + size_t end = name.size(); + size_t start; + + for (int i = 3; i >= 1; --i) { + start = name.find_last_of(' ', end - 1); + if (start == std::string::npos) { + break; + } + std::string word = name.substr(start + 1, end - start - 1); + + if (!update_font_desc_with_word(font_desc, word)) { + break; + } + + end = start; + } + font_desc.font_name = g_strdup(name.substr(0, end).c_str()); +} + PopplerAnnot *_poppler_annot_free_text_new(Annot *annot) { - return _poppler_create_annot(POPPLER_TYPE_ANNOT_FREE_TEXT, annot); + PopplerAnnot *poppler_annot = _poppler_create_annot(POPPLER_TYPE_ANNOT_FREE_TEXT, annot); + PopplerAnnotFreeText *ft_annot = POPPLER_ANNOT_FREE_TEXT(poppler_annot); + std::unique_ptr da = ((AnnotFreeText *)annot)->getDefaultAppearance(); + PopplerFontDescription *desc = nullptr; + if (da->getFontName().isName()) { + desc = poppler_font_description_new(da->getFontName().getName()); + desc->size_pt = da->getFontPtSize(); + + /* Attempt to resolve the actual font name. */ + Form *form = annot->getDoc()->getCatalog()->getCreateForm(); + if (form) { + GfxResources *res = form->getDefaultResources(); + if (res) { + std::shared_ptr font = res->lookupFont(desc->font_name); + if (font) { + const std::optional &fontName = font->getName(); + if (fontName) { + poppler_font_name_to_description(fontName.value(), *desc); + } + } + } + } + + std::map::const_iterator fallback_font = fallback_fonts.find(std::string(desc->font_name)); + if (fallback_font != fallback_fonts.end()) { + desc->font_name = g_strdup(fallback_font->second.c_str()); + } + } + + ft_annot->font_desc = desc; + + const AnnotColor *ac = da->getFontColor(); + + if (ac) { + PopplerColor *font_color = _poppler_convert_annot_color_to_poppler_color(ac); + ft_annot->font_color = *font_color; + poppler_color_free(font_color); + } + + return poppler_annot; +} + +/** + * poppler_annot_free_text_new: + * @doc: a #PopplerDocument + * @rect: a #PopplerRectangle + * + * Creates a new Free Text annotation that will be + * located on @rect when added to a page. See + * poppler_page_add_annot(). It initially has no content. Font family, size and + * color are initially undefined and must be set, see + * poppler_annot_free_text_set_font_desc() and + * poppler_annot_free_text_set_font_color(). + * + * Returns: (transfer full): A newly created #PopplerAnnotFreeText annotation + */ +PopplerAnnot *poppler_annot_free_text_new(PopplerDocument *doc, PopplerRectangle *rect) +{ + Annot *annot; + PDFRectangle pdf_rect(rect->x1, rect->y1, rect->x2, rect->y2); + + annot = new AnnotFreeText(doc->doc, &pdf_rect); + + PopplerAnnot *poppler_annot = _poppler_annot_free_text_new(annot); + + return poppler_annot; } static void poppler_annot_file_attachment_init(PopplerAnnotFileAttachment *poppler_annot) { } @@ -936,49 +1132,6 @@ void poppler_annot_set_flags(PopplerAnnot *poppler_annot, PopplerAnnotFlag flags poppler_annot->annot->setFlags((guint)flags); } -static PopplerColor *create_poppler_color_from_annot_color(AnnotColor *color) -{ - PopplerColor *poppler_color = nullptr; - - if (color) { - const double *values = color->getValues(); - - switch (color->getSpace()) { - case AnnotColor::colorGray: - poppler_color = g_new(PopplerColor, 1); - - poppler_color->red = (guint16)(values[0] * 65535); - poppler_color->green = poppler_color->red; - poppler_color->blue = poppler_color->red; - - break; - case AnnotColor::colorRGB: - poppler_color = g_new(PopplerColor, 1); - - poppler_color->red = (guint16)(values[0] * 65535); - poppler_color->green = (guint16)(values[1] * 65535); - poppler_color->blue = (guint16)(values[2] * 65535); - - break; - case AnnotColor::colorCMYK: - g_warning("Unsupported Annot Color: colorCMYK"); - case AnnotColor::colorTransparent: - break; - } - } - - return poppler_color; -} - -static std::unique_ptr create_annot_color_from_poppler_color(PopplerColor *poppler_color) -{ - if (!poppler_color) { - return nullptr; - } - - return std::make_unique((double)poppler_color->red / 65535, (double)poppler_color->green / 65535, (double)poppler_color->blue / 65535); -} - /** * poppler_annot_get_color: * @poppler_annot: a #PopplerAnnot @@ -992,7 +1145,7 @@ PopplerColor *poppler_annot_get_color(PopplerAnnot *poppler_annot) { g_return_val_if_fail(POPPLER_IS_ANNOT(poppler_annot), NULL); - return create_poppler_color_from_annot_color(poppler_annot->annot->getColor()); + return _poppler_convert_annot_color_to_poppler_color(poppler_annot->annot->getColor()); } /** @@ -1006,7 +1159,7 @@ PopplerColor *poppler_annot_get_color(PopplerAnnot *poppler_annot) */ void poppler_annot_set_color(PopplerAnnot *poppler_annot, PopplerColor *poppler_color) { - poppler_annot->annot->setColor(create_annot_color_from_poppler_color(poppler_color)); + poppler_annot->annot->setColor(_poppler_convert_poppler_color_to_annot_color(poppler_color)); } /** @@ -1685,7 +1838,7 @@ void poppler_annot_text_markup_set_quadrilaterals(PopplerAnnotTextMarkup *popple delete quads_temp; } - annot->setQuadrilaterals(quads); + annot->setQuadrilaterals(*quads); delete quads; } @@ -1789,6 +1942,151 @@ PopplerAnnotCalloutLine *poppler_annot_free_text_get_callout_line(PopplerAnnotFr return nullptr; } +static std::string poppler_font_description_to_style(PopplerFontDescription *font_desc) +{ + std::string style; + std::function add_style = [&style](const char *a) { + if (strcmp(a, "") != 0) { + if (!style.empty()) { + style += " "; + } + style += a; + } + }; + + /* Stretch */ + add_style(stretch_to_str[font_desc->stretch]); + + /* Don't use a map so that intermediate pango weights are correctly mapped */ + PopplerWeight w = font_desc->weight; + if (w <= POPPLER_WEIGHT_THIN) { + add_style("Thin"); + } else if (w <= POPPLER_WEIGHT_ULTRALIGHT) { + add_style("UltraLight"); + } else if (w <= POPPLER_WEIGHT_LIGHT) { + add_style("Light"); + } else if (w <= POPPLER_WEIGHT_NORMAL) { + add_style(""); + } else if (w <= POPPLER_WEIGHT_MEDIUM) { + add_style("Medium"); + } else if (w <= POPPLER_WEIGHT_SEMIBOLD) { + add_style("SemiBold"); + } else if (w <= POPPLER_WEIGHT_BOLD) { + add_style("Bold"); + } else if (w <= POPPLER_WEIGHT_ULTRABOLD) { + add_style("UltraBold"); + } else { + add_style("Heavy"); + } + + /* Style, i.e. italic, oblique or normal */ + if (font_desc->style == POPPLER_STYLE_ITALIC) { + add_style("Italic"); + } else if (font_desc->style == POPPLER_STYLE_OBLIQUE) { + add_style("Oblique"); + } + + return style; +} + +static void poppler_annot_free_text_set_da_to_native(PopplerAnnotFreeText *poppler_annot) +{ + Annot *annot = POPPLER_ANNOT(poppler_annot)->annot; + + std::string font_name = "Sans"; + double size = 11.; + + if (poppler_annot->font_desc) { + const char *family = poppler_annot->font_desc->font_name; + const std::string style = poppler_font_description_to_style(poppler_annot->font_desc); + + Form *form = annot->getDoc()->getCatalog()->getCreateForm(); + if (form) { + font_name = form->findFontInDefaultResources(family, style); + if (font_name.empty()) { + font_name = form->addFontToDefaultResources(family, style).fontName; + } + + if (!font_name.empty()) { + form->ensureFontsForAllCharacters(annot->getContents(), font_name); + } + } + size = poppler_annot->font_desc->size_pt; + } + + DefaultAppearance da { { objName, font_name.c_str() }, size, _poppler_convert_poppler_color_to_annot_color(&(poppler_annot->font_color)) }; + ((AnnotFreeText *)annot)->setDefaultAppearance(da); +} + +/** + * poppler_annot_free_text_set_font_desc: + * @poppler_annot: a #PopplerAnnotFreeText + * @font_desc: a #PopplerFontDescription + * + * Sets the font description (i.e. font family name, style, weight, stretch and size). + * + * Since: 24.12.0 + **/ +void poppler_annot_free_text_set_font_desc(PopplerAnnotFreeText *poppler_annot, PopplerFontDescription *font_desc) +{ + if (poppler_annot->font_desc) { + poppler_font_description_free(poppler_annot->font_desc); + } + poppler_annot->font_desc = poppler_font_description_copy(font_desc); + poppler_annot_free_text_set_da_to_native(poppler_annot); +} + +/** + * poppler_annot_free_text_get_font_desc: + * @poppler_annot: a #PopplerAnnotFreeText + * + * Gets the font description (i.e. font family name, style, weight, stretch and size). + * + * Returns: (nullable) (transfer full): a copy of the annotation font description, or NULL if there is + * no font description set. + * + * Since: 24.12.0 + **/ +PopplerFontDescription *poppler_annot_free_text_get_font_desc(PopplerAnnotFreeText *poppler_annot) +{ + if (poppler_annot->font_desc) { + return poppler_font_description_copy(poppler_annot->font_desc); + } + return nullptr; +} + +/** + * poppler_annot_free_text_set_font_color: + * @poppler_annot: a #PopplerAnnotFreeText + * @color: a #PopplerColor + * + * Sets the font color. + * + * Since: 24.12.0 + **/ +void poppler_annot_free_text_set_font_color(PopplerAnnotFreeText *poppler_annot, PopplerColor *color) +{ + poppler_annot->font_color = *color; + poppler_annot_free_text_set_da_to_native(poppler_annot); +} + +/** + * poppler_annot_free_text_get_font_color: + * @poppler_annot: a #PopplerAnnotFreeText + * + * Gets the font color. + * + * Returns: (transfer full): a copy of the font's #PopplerColor. + * + * Since: 24.12.0 + **/ +PopplerColor *poppler_annot_free_text_get_font_color(PopplerAnnotFreeText *poppler_annot) +{ + PopplerColor *color = g_new(PopplerColor, 1); + *color = poppler_annot->font_color; + return color; +} + /* PopplerAnnotFileAttachment */ /** * poppler_annot_file_attachment_get_attachment: @@ -1976,7 +2274,7 @@ static PopplerColor *poppler_annot_geometry_get_interior_color(PopplerAnnot *pop annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); - return create_poppler_color_from_annot_color(annot->getInteriorColor()); + return _poppler_convert_annot_color_to_poppler_color(annot->getInteriorColor()); } static void poppler_annot_geometry_set_interior_color(PopplerAnnot *poppler_annot, PopplerColor *poppler_color) @@ -1985,7 +2283,7 @@ static void poppler_annot_geometry_set_interior_color(PopplerAnnot *poppler_anno annot = static_cast(POPPLER_ANNOT(poppler_annot)->annot); - annot->setInteriorColor(create_annot_color_from_poppler_color(poppler_color)); + annot->setInteriorColor(_poppler_convert_poppler_color_to_annot_color(poppler_color)); } /* PopplerAnnotCircle */ @@ -2199,3 +2497,108 @@ gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, return TRUE; } + +/** + * poppler_annot_get_border_width: + * @poppler_annot: a #PopplerAnnot + * @width: a valid pointer to a double + * + * Returns the border width of the annotation. Some PDF editors set a border + * width even if the border is not actually drawn. + * + * Returns: true and sets @border_width to the actual border width if a border + * is defined, otherwise returns false and sets @border_width to 0. + * + * Since: 24.12.0 + */ +gboolean poppler_annot_get_border_width(PopplerAnnot *poppler_annot, double *width) +{ + Annot *annot = poppler_annot->annot; + AnnotBorder *b = annot->getBorder(); + if (b) { + *width = b->getWidth(); + return TRUE; + } else { + *width = 0.; + return FALSE; + } +} +/** + * poppler_annot_set_border_width: + * @poppler_annot: a #PopplerAnnot + * @width: the new border width + * + * Sets the border width of the annotation. Since there is currently no + * mechanism in the GLib binding to control the appearance of the border width, + * this should generally only be used to disable the border, although the + * API might be completed in the future. + * + * Since: 24.12.0 + */ +void poppler_annot_set_border_width(PopplerAnnot *poppler_annot, double width) +{ + Annot *annot = poppler_annot->annot; + std::unique_ptr border = std::make_unique(); + border->setWidth(width); + annot->setBorder(std::move(border)); +} + +/** + * SECTION:poppler-font-description + * @short_description: FontDescription + * @title: PopplerFontDescription + */ + +/* PopplerFontDescription type */ +G_DEFINE_BOXED_TYPE(PopplerFontDescription, poppler_font_description, poppler_font_description_copy, poppler_font_description_free) + +/** + * poppler_font_description_new: + * @font_name: the family name of the font + * + * Creates a new #PopplerFontDescriptions + * + * Returns: a new #PopplerFontDescription, use poppler_font_description_free() to free it + */ +PopplerFontDescription *poppler_font_description_new(const char *font_name) +{ + PopplerFontDescription *font_desc = (PopplerFontDescription *)g_new0(PopplerFontDescription, 1); + font_desc->font_name = g_strdup(font_name); + font_desc->size_pt = 11.; + font_desc->stretch = POPPLER_STRETCH_NORMAL; + font_desc->style = POPPLER_STYLE_NORMAL; + font_desc->weight = POPPLER_WEIGHT_NORMAL; + return font_desc; +} + +/** + * poppler_font_description_free: + * @font_desc: a #PopplerFontDescription + * + * Frees the given #PopplerFontDescription + */ +void poppler_font_description_free(PopplerFontDescription *font_desc) +{ + g_free(font_desc->font_name); + g_free(font_desc); +} + +/** + * poppler_font_description_copy: + * @font_desc: a #PopplerFontDescription to copy + * + * Creates a copy of @font_desc + * + * Returns: a new allocated copy of @font_desc + */ +PopplerFontDescription *poppler_font_description_copy(PopplerFontDescription *font_desc) +{ + PopplerFontDescription *new_font_desc; + + new_font_desc = g_new(PopplerFontDescription, 1); + *new_font_desc = *font_desc; + + new_font_desc->font_name = g_strdup(font_desc->font_name); + + return new_font_desc; +} diff --git a/glib/poppler-annot.h b/glib/poppler-annot.h index 509ecdf..a0bf6e9 100644 --- a/glib/poppler-annot.h +++ b/glib/poppler-annot.h @@ -2,6 +2,7 @@ * * Copyright (C) 2007 Inigo Martinez * Copyright (C) 2009 Carlos Garcia Campos + * Copyright (C) 2025 Markus Göllnitz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ #ifndef __POPPLER_ANNOT_H__ #define __POPPLER_ANNOT_H__ +#include #include #include "poppler.h" @@ -47,7 +49,7 @@ G_BEGIN_DECLS #define POPPLER_IS_ANNOT_FREE_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_FREE_TEXT)) #define POPPLER_TYPE_ANNOT_FILE_ATTACHMENT (poppler_annot_file_attachment_get_type()) -#define POPPLER_ANNOT_FILE_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_MARKUP, PopplerAnnotFileAttachment)) +#define POPPLER_ANNOT_FILE_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_ANNOT_FILE_ATTACHMENT, PopplerAnnotFileAttachment)) #define POPPLER_IS_ANNOT_FILE_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_ANNOT_FILE_ATTACHMENT)) #define POPPLER_TYPE_ANNOT_MOVIE (poppler_annot_movie_get_type()) @@ -192,6 +194,67 @@ typedef enum POPPLER_ANNOT_STAMP_ICON_NONE } PopplerAnnotStampIcon; +/* The next three enums are value-compatible with pango equivalents. */ + +typedef enum +{ + POPPLER_STRETCH_ULTRA_CONDENSED, + POPPLER_STRETCH_EXTRA_CONDENSED, + POPPLER_STRETCH_CONDENSED, + POPPLER_STRETCH_SEMI_CONDENSED, + POPPLER_STRETCH_NORMAL, + POPPLER_STRETCH_SEMI_EXPANDED, + POPPLER_STRETCH_EXPANDED, + POPPLER_STRETCH_EXTRA_EXPANDED, + POPPLER_STRETCH_ULTRA_EXPANDED +} PopplerStretch; + +typedef enum +{ + POPPLER_WEIGHT_THIN = 100, + POPPLER_WEIGHT_ULTRALIGHT = 200, + POPPLER_WEIGHT_LIGHT = 300, + POPPLER_WEIGHT_NORMAL = 400, + POPPLER_WEIGHT_MEDIUM = 500, + POPPLER_WEIGHT_SEMIBOLD = 600, + POPPLER_WEIGHT_BOLD = 700, + POPPLER_WEIGHT_ULTRABOLD = 800, + POPPLER_WEIGHT_HEAVY = 900 +} PopplerWeight; + +typedef enum +{ + POPPLER_STYLE_NORMAL, + POPPLER_STYLE_OBLIQUE, + POPPLER_STYLE_ITALIC +} PopplerStyle; + +/** + * PopplerFontDescription: + * @font_name: name of font family + * @size_pt: size of font in pt + * @stretch: a #PopplerStretch representing stretch of the font + * @weight: a #PopplerWeight representing weight of the font + * @style: a #PopplerStyle representing style of the font + * + * A #PopplerFontDescription structure represents the description + * of a font. When used together with Pango, all the fields are + * value-compatible with pango equivalent, although Pango font + * descriptions may contain more information. + * + * This type supports g_autoptr + * + * Since: 24.12.0 + */ +struct _PopplerFontDescription +{ + char *font_name; + double size_pt; + PopplerStretch stretch; + PopplerWeight weight; + PopplerStyle style; +}; + POPPLER_PUBLIC GType poppler_annot_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC @@ -218,6 +281,10 @@ POPPLER_PUBLIC void poppler_annot_get_rectangle(PopplerAnnot *poppler_annot, PopplerRectangle *poppler_rect); POPPLER_PUBLIC void poppler_annot_set_rectangle(PopplerAnnot *poppler_annot, PopplerRectangle *poppler_rect); +POPPLER_PUBLIC +gboolean poppler_annot_get_border_width(PopplerAnnot *poppler_annot, double *width); +POPPLER_PUBLIC +void poppler_annot_set_border_width(PopplerAnnot *poppler_annot, double width); /* PopplerAnnotMarkup */ POPPLER_PUBLIC @@ -287,9 +354,29 @@ GArray *poppler_annot_text_markup_get_quadrilaterals(PopplerAnnotTextMarkup *pop POPPLER_PUBLIC GType poppler_annot_free_text_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC +PopplerAnnot *poppler_annot_free_text_new(PopplerDocument *doc, PopplerRectangle *rect); +POPPLER_PUBLIC PopplerAnnotFreeTextQuadding poppler_annot_free_text_get_quadding(PopplerAnnotFreeText *poppler_annot); POPPLER_PUBLIC PopplerAnnotCalloutLine *poppler_annot_free_text_get_callout_line(PopplerAnnotFreeText *poppler_annot); +POPPLER_PUBLIC +void poppler_annot_free_text_set_font_desc(PopplerAnnotFreeText *poppler_annot, PopplerFontDescription *font_desc); +POPPLER_PUBLIC +PopplerFontDescription *poppler_annot_free_text_get_font_desc(PopplerAnnotFreeText *poppler_annot); +POPPLER_PUBLIC +void poppler_annot_free_text_set_font_color(PopplerAnnotFreeText *poppler_annot, PopplerColor *color); +POPPLER_PUBLIC +PopplerColor *poppler_annot_free_text_get_font_color(PopplerAnnotFreeText *poppler_annot); + +/* Fonts Handling for AnnotFreeText */ +POPPLER_PUBLIC +GType poppler_font_description_get_type(void) G_GNUC_CONST; +POPPLER_PUBLIC +PopplerFontDescription *poppler_font_description_new(const char *font_name); +POPPLER_PUBLIC +void poppler_font_description_free(PopplerFontDescription *font_desc); +POPPLER_PUBLIC +PopplerFontDescription *poppler_font_description_copy(PopplerFontDescription *font_desc); /* PopplerAnnotFileAttachment */ POPPLER_PUBLIC @@ -365,4 +452,6 @@ gboolean poppler_annot_stamp_set_custom_image(PopplerAnnotStamp *poppler_annot, G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerFontDescription, poppler_font_description_free) + #endif /* __POPPLER_ANNOT_H__ */ diff --git a/glib/poppler-attachment.cc b/glib/poppler-attachment.cc index 51a6f95..1101e9a 100644 --- a/glib/poppler-attachment.cc +++ b/glib/poppler-attachment.cc @@ -1,5 +1,6 @@ /* poppler-attachment.cc: glib wrapper for poppler * Copyright (C) 2006, Red Hat, Inc. + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +38,7 @@ struct PopplerAttachmentPrivate { - Object obj_stream {}; + Object obj_stream; GDateTime *mtime; GDateTime *ctime; }; @@ -358,7 +359,9 @@ gboolean poppler_attachment_save_to_callback(PopplerAttachment *attachment, Popp priv = GET_PRIVATE(attachment); stream = priv->obj_stream.getStream(); - stream->reset(); + if (!stream->reset()) { + return FALSE; + } do { int data; diff --git a/glib/poppler-document.cc b/glib/poppler-document.cc index ae9cb5f..4d8ef63 100644 --- a/glib/poppler-document.cc +++ b/glib/poppler-document.cc @@ -4,9 +4,10 @@ * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018, 2019, 2021, 2022 Marek Kasik * Copyright (C) 2019 Masamichi Hosoda - * Copyright (C) 2019, 2021 Oliver Sander + * Copyright (C) 2019, 2021, 2024 Oliver Sander * Copyright (C) 2020, 2022 Albert Astals Cid * Copyright (C) 2021 André Guerreiro + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,6 +57,7 @@ # include # include # include +# include "UTF.h" #endif #include "poppler.h" @@ -675,7 +677,7 @@ static void poppler_document_finalize(GObject *object) poppler_document_layers_free(document); delete document->output_dev; delete document->doc; - delete document->initer.release(); + document->initer.reset(); G_OBJECT_CLASS(poppler_document_parent_class)->finalize(object); } @@ -1097,9 +1099,9 @@ char *_poppler_goo_string_to_utf8(const GooString *s) char *result; - if (s->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(s->toStr())) { result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr); - } else if (s->hasUnicodeMarkerLE()) { + } else if (hasUnicodeByteOrderMarkLE(s->toStr())) { result = g_convert(s->c_str() + 2, s->getLength() - 2, "UTF-8", "UTF-16LE", nullptr, nullptr, nullptr); } else { int len; @@ -1121,7 +1123,7 @@ char *_poppler_goo_string_to_utf8(const GooString *s) return result; } -static GooString *_poppler_goo_string_from_utf8(const gchar *src) +static std::unique_ptr _poppler_goo_string_from_utf8(const gchar *src) { if (src == nullptr) { return nullptr; @@ -1134,11 +1136,11 @@ static GooString *_poppler_goo_string_from_utf8(const gchar *src) return nullptr; } - GooString *result = new GooString(utf16, outlen); + std::unique_ptr result = std::make_unique(utf16, outlen); g_free(utf16); - if (!result->hasUnicodeMarker()) { - result->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(result->toStr())) { + prependUnicodeByteOrderMark(result->toNonConstStr()); } return result; @@ -1335,16 +1337,14 @@ void poppler_document_set_title(PopplerDocument *document, const gchar *title) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_title; - if (!title) { - goo_title = nullptr; - } else { + std::unique_ptr goo_title; + if (title) { goo_title = _poppler_goo_string_from_utf8(title); if (!goo_title) { return; } } - document->doc->setDocInfoTitle(goo_title); + document->doc->setDocInfoTitle(std::move(goo_title)); } /** @@ -1380,16 +1380,14 @@ void poppler_document_set_author(PopplerDocument *document, const gchar *author) { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_author; - if (!author) { - goo_author = nullptr; - } else { + std::unique_ptr goo_author; + if (author) { goo_author = _poppler_goo_string_from_utf8(author); if (!goo_author) { return; } } - document->doc->setDocInfoAuthor(goo_author); + document->doc->setDocInfoAuthor(std::move(goo_author)); } /** @@ -1425,16 +1423,14 @@ void poppler_document_set_subject(PopplerDocument *document, const gchar *subjec { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_subject; - if (!subject) { - goo_subject = nullptr; - } else { + std::unique_ptr goo_subject; + if (subject) { goo_subject = _poppler_goo_string_from_utf8(subject); if (!goo_subject) { return; } } - document->doc->setDocInfoSubject(goo_subject); + document->doc->setDocInfoSubject(std::move(goo_subject)); } /** @@ -1470,16 +1466,14 @@ void poppler_document_set_keywords(PopplerDocument *document, const gchar *keywo { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_keywords; - if (!keywords) { - goo_keywords = nullptr; - } else { + std::unique_ptr goo_keywords; + if (keywords) { goo_keywords = _poppler_goo_string_from_utf8(keywords); if (!goo_keywords) { return; } } - document->doc->setDocInfoKeywords(goo_keywords); + document->doc->setDocInfoKeywords(std::move(goo_keywords)); } /** @@ -1517,16 +1511,14 @@ void poppler_document_set_creator(PopplerDocument *document, const gchar *creato { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_creator; - if (!creator) { - goo_creator = nullptr; - } else { + std::unique_ptr goo_creator; + if (creator) { goo_creator = _poppler_goo_string_from_utf8(creator); if (!goo_creator) { return; } } - document->doc->setDocInfoCreator(goo_creator); + document->doc->setDocInfoCreator(std::move(goo_creator)); } /** @@ -1564,16 +1556,14 @@ void poppler_document_set_producer(PopplerDocument *document, const gchar *produ { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *goo_producer; - if (!producer) { - goo_producer = nullptr; - } else { + std::unique_ptr goo_producer; + if (producer) { goo_producer = _poppler_goo_string_from_utf8(producer); if (!goo_producer) { return; } } - document->doc->setDocInfoProducer(goo_producer); + document->doc->setDocInfoProducer(std::move(goo_producer)); } /** @@ -1615,8 +1605,8 @@ void poppler_document_set_creation_date(PopplerDocument *document, time_t creati { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *str = creation_date == (time_t)-1 ? nullptr : timeToDateString(&creation_date); - document->doc->setDocInfoCreatDate(str); + std::unique_ptr str = creation_date == (time_t)-1 ? nullptr : timeToDateString(&creation_date); + document->doc->setDocInfoCreatDate(std::move(str)); } /** @@ -1656,13 +1646,13 @@ void poppler_document_set_creation_date_time(PopplerDocument *document, GDateTim { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *str = nullptr; + std::unique_ptr str = nullptr; if (creation_datetime) { str = _poppler_convert_date_time_to_pdf_date(creation_datetime); } - document->doc->setDocInfoCreatDate(str); + document->doc->setDocInfoCreatDate(std::move(str)); } /** @@ -1704,8 +1694,8 @@ void poppler_document_set_modification_date(PopplerDocument *document, time_t mo { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *str = modification_date == (time_t)-1 ? nullptr : timeToDateString(&modification_date); - document->doc->setDocInfoModDate(str); + std::unique_ptr str = modification_date == (time_t)-1 ? nullptr : timeToDateString(&modification_date); + document->doc->setDocInfoModDate(std::move(str)); } /** @@ -1745,13 +1735,13 @@ void poppler_document_set_modification_date_time(PopplerDocument *document, GDat { g_return_if_fail(POPPLER_IS_DOCUMENT(document)); - GooString *str = nullptr; + std::unique_ptr str = nullptr; if (modification_datetime) { str = _poppler_convert_date_time_to_pdf_date(modification_datetime); } - document->doc->setDocInfoModDate(str); + document->doc->setDocInfoModDate(std::move(str)); } /** @@ -2226,7 +2216,7 @@ void poppler_document_reset_form(PopplerDocument *document, GList *fields, gbool if (form) { for (iter = fields; iter != nullptr; iter = iter->next) { - list.emplace_back(std::string((char *)iter->data)); + list.emplace_back((char *)iter->data); } form->reset(list, exclude_fields); @@ -2591,7 +2581,13 @@ static void poppler_document_class_init(PopplerDocumentClass *klass) static void poppler_document_init(PopplerDocument *document) { } -/* PopplerIndexIter: For determining the index of a tree */ +/** + * PopplerIndexIter: + * + * Interface for getting the Index of a poppler_document + * + * Since 24.10 this type supports g_autoptr + */ struct _PopplerIndexIter { PopplerDocument *document; @@ -2772,7 +2768,8 @@ PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter) item = (*iter->items)[iter->index]; link_action = item->getAction(); - title = unicode_to_char(item->getTitle(), item->getTitleLength()); + const std::vector &itemTitle = item->getTitle(); + title = unicode_to_char(itemTitle.data(), itemTitle.size()); action = _poppler_action_new(iter->document, link_action, title); g_free(title); @@ -2817,6 +2814,11 @@ void poppler_index_iter_free(PopplerIndexIter *iter) g_slice_free(PopplerIndexIter, iter); } +/** + * PopplerFontsIter: + * + * Since 24.10 this type supports g_autoptr + */ struct _PopplerFontsIter { std::vector items; @@ -3083,6 +3085,13 @@ struct _PopplerFontInfoClass GObjectClass parent_class; }; +/** + * PopplerFontInfo: + * + * Interface for getting the Fonts of a poppler_document + * + * Since 24.10 this type supports g_autoptr + */ G_DEFINE_TYPE(PopplerFontInfo, poppler_font_info, G_TYPE_OBJECT) static void poppler_font_info_finalize(GObject *object); @@ -3145,7 +3154,10 @@ PopplerFontInfo *poppler_font_info_new(PopplerDocument *document) * * * font_info = poppler_font_info_new (document); - * while (poppler_font_info_scan (font_info, 20, &fonts_iter)) { + * scanned_pages = 0; + * while (scanned_pages <= poppler_document_get_n_pages(document)) { + * poppler_font_info_scan (font_info, 20, &fonts_iter); + * scanned_pages += 20; * if (!fonts_iter) * continue; /* No fonts found in these 20 pages */ * do { @@ -3156,7 +3168,7 @@ PopplerFontInfo *poppler_font_info_new(PopplerDocument *document) * } * * - * Returns: %TRUE, if there are more fonts left to scan + * Returns: %TRUE, if fonts were found */ gboolean poppler_font_info_scan(PopplerFontInfo *font_info, int n_pages, PopplerFontsIter **iter) { @@ -3210,12 +3222,11 @@ static void layer_free(Layer *layer) g_slice_free(Layer, layer); } -static GList *get_optional_content_rbgroups(OCGs *ocg) +static GList *get_optional_content_rbgroups(const OCGs *ocg) { - Array *rb; GList *groups = nullptr; - rb = ocg->getRBGroupsArray(); + const Array *rb = ocg->getRBGroupsArray(); if (rb) { int i, j; @@ -3264,7 +3275,7 @@ GList *_poppler_document_get_layer_rbgroup(PopplerDocument *document, Layer *lay return nullptr; } -static GList *get_optional_content_items_sorted(OCGs *ocg, Layer *parent, Array *order) +static GList *get_optional_content_items_sorted(const OCGs *ocg, Layer *parent, const Array *order) { GList *items = nullptr; Layer *last_item = parent; @@ -3297,12 +3308,11 @@ static GList *get_optional_content_items_sorted(OCGs *ocg, Layer *parent, Array return g_list_reverse(items); } -static GList *get_optional_content_items(OCGs *ocg) +static GList *get_optional_content_items(const OCGs *ocg) { - Array *order; GList *items = nullptr; - order = ocg->getOrderArray(); + const Array *order = ocg->getOrderArray(); if (order) { items = get_optional_content_items_sorted(ocg, nullptr, order); @@ -3325,7 +3335,7 @@ GList *_poppler_document_get_layers(PopplerDocument *document) { if (!document->layers) { Catalog *catalog = document->doc->getCatalog(); - OCGs *ocg = catalog->getOptContentConfig(); + const OCGs *ocg = catalog->getOptContentConfig(); if (!ocg) { return nullptr; @@ -3351,7 +3361,13 @@ static void poppler_document_layers_free(PopplerDocument *document) document->layers_rbgroups = nullptr; } -/* PopplerLayersIter */ +/** + * PopplerLayersIter: + * + * Interface for getting the Layers of a poppler_document + * + * Since 24.10 this type supports g_autoptr + */ struct _PopplerLayersIter { PopplerDocument *document; @@ -3539,6 +3555,13 @@ struct _PopplerPSFileClass GObjectClass parent_class; }; +/** + * PopplerPsFile: + * + * Interface for exporting to ps + * + * Since 24.10 this type supports g_autoptr + */ G_DEFINE_TYPE(PopplerPSFile, poppler_ps_file, G_TYPE_OBJECT) static void poppler_ps_file_finalize(GObject *object); @@ -3730,7 +3753,7 @@ gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate gchar *date_string; gboolean retval; - if (date->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(date->toStr())) { date_string = g_convert(date->c_str() + 2, date->getLength() - 2, "UTF-8", "UTF-16BE", nullptr, nullptr, nullptr); } else { date_string = g_strndup(date->c_str(), date->getLength()); @@ -3762,15 +3785,11 @@ GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date) gchar *identifier; identifier = g_strdup_printf("%c%02u:%02u", tz, tzHours, tzMins); -#if GLIB_CHECK_VERSION(2, 68, 0) time_zone = g_time_zone_new_identifier(identifier); if (!time_zone) { g_debug("Failed to create time zone for identifier \"%s\"", identifier); time_zone = g_time_zone_new_utc(); } -#else - time_zone = g_time_zone_new(identifier); -#endif g_free(identifier); } else if (tz == '\0' || tz == 'Z') { time_zone = g_time_zone_new_utc(); @@ -3794,11 +3813,11 @@ GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date) * * Returns: The converted date **/ -GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime) +std::unique_ptr _poppler_convert_date_time_to_pdf_date(GDateTime *datetime) { int offset_min; gchar *date_str; - std::unique_ptr out_str; + std::string out_str; offset_min = g_date_time_get_utc_offset(datetime) / 1000000 / 60; date_str = g_date_time_format(datetime, "D:%Y%m%d%H%M%S"); @@ -3812,5 +3831,105 @@ GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime) } g_free(date_str); - return out_str.release(); + return std::make_unique(std::move(out_str)); +} + +static void _poppler_sign_document_thread(GTask *task, PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable) +{ + const PopplerCertificateInfo *certificate_info; + const char *signing_data_signature_text; + const PopplerColor *font_color; + const PopplerColor *border_color; + const PopplerColor *background_color; + gboolean ret; + + g_return_if_fail(POPPLER_IS_DOCUMENT(document)); + g_return_if_fail(signing_data != nullptr); + + signing_data_signature_text = poppler_signing_data_get_signature_text(signing_data); + if (signing_data_signature_text == nullptr) { + g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "No signature given"); + return; + } + + certificate_info = poppler_signing_data_get_certificate_info(signing_data); + if (certificate_info == nullptr) { + g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "Invalid certificate information provided for signing"); + return; + } + + PopplerPage *page = poppler_document_get_page(document, poppler_signing_data_get_page(signing_data)); + if (page == nullptr) { + g_task_return_new_error(task, POPPLER_ERROR, POPPLER_ERROR_SIGNING, "Invalid page number selected for signing"); + return; + } + + font_color = poppler_signing_data_get_font_color(signing_data); + border_color = poppler_signing_data_get_border_color(signing_data); + background_color = poppler_signing_data_get_background_color(signing_data); + + std::unique_ptr signature_text = std::make_unique(utf8ToUtf16WithBom(signing_data_signature_text)); + std::unique_ptr signature_text_left = std::make_unique(utf8ToUtf16WithBom(poppler_signing_data_get_signature_text_left(signing_data))); + auto field_partial_name = std::make_unique(poppler_signing_data_get_field_partial_name(signing_data), strlen(poppler_signing_data_get_field_partial_name(signing_data))); + const auto owner_pwd = std::optional(poppler_signing_data_get_document_owner_password(signing_data)); + const auto user_pwd = std::optional(poppler_signing_data_get_document_user_password(signing_data)); + const auto reason = std::unique_ptr(poppler_signing_data_get_reason(signing_data) ? new GooString(poppler_signing_data_get_reason(signing_data), strlen(poppler_signing_data_get_reason(signing_data))) : nullptr); + const auto location = std::unique_ptr(poppler_signing_data_get_location(signing_data) ? new GooString(poppler_signing_data_get_location(signing_data), strlen(poppler_signing_data_get_location(signing_data))) : nullptr); + const PopplerRectangle *rect = poppler_signing_data_get_signature_rectangle(signing_data); + + ret = !document->doc + ->sign(poppler_signing_data_get_destination_filename(signing_data), poppler_certificate_info_get_id((PopplerCertificateInfo *)certificate_info), + poppler_signing_data_get_password(signing_data) ? poppler_signing_data_get_password(signing_data) : "", std::move(field_partial_name), poppler_signing_data_get_page(signing_data) + 1, + PDFRectangle(rect->x1, rect->y1, rect->x2, rect->y2), *signature_text, *signature_text_left, poppler_signing_data_get_font_size(signing_data), poppler_signing_data_get_left_font_size(signing_data), + _poppler_convert_poppler_color_to_annot_color(font_color), poppler_signing_data_get_border_width(signing_data), _poppler_convert_poppler_color_to_annot_color(border_color), + _poppler_convert_poppler_color_to_annot_color(background_color), reason.get(), location.get(), poppler_signing_data_get_image_path(signing_data) ? poppler_signing_data_get_image_path(signing_data) : "", owner_pwd, + user_pwd) + .has_value(); + + g_task_return_boolean(task, ret); +} + +/** + * poppler_document_sign: + * @document: a #PopplerDocument + * @signing_data: a #PopplerSigningData + * @cancellable: a #GCancellable + * @callback: a #GAsyncReadyCallback + * @user_data: user data used by callback function + * + * Sign #document using #signing_data. + * + * Since: 23.07.0 + **/ +void poppler_document_sign(PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) +{ + GTask *task; + + g_return_if_fail(POPPLER_IS_DOCUMENT(document)); + g_return_if_fail(signing_data != nullptr); + + task = g_task_new(document, cancellable, callback, user_data); + g_task_set_task_data(task, (void *)signing_data, nullptr); + + g_task_run_in_thread(task, (GTaskThreadFunc)_poppler_sign_document_thread); + g_object_unref(task); +} + +/** + * poppler_document_sign_finish: + * @document: a #PopplerDocument + * @result: a #GAsyncResult + * @error: a #GError + * + * Finish poppler_sign_document and get return status or error. + * + * Returns: %TRUE on successful signing a document, otherwise %FALSE and error is set. + * + * Since: 23.07.0 + **/ +gboolean poppler_document_sign_finish(PopplerDocument *document, GAsyncResult *result, GError **error) +{ + g_return_val_if_fail(g_task_is_valid(result, document), FALSE); + + return g_task_propagate_boolean(G_TASK(result), error); } diff --git a/glib/poppler-document.h b/glib/poppler-document.h index 3dabfa2..acf7669 100644 --- a/glib/poppler-document.h +++ b/glib/poppler-document.h @@ -423,7 +423,6 @@ GList *poppler_document_get_signature_fields(PopplerDocument *document); POPPLER_PUBLIC gint poppler_document_get_n_signatures(const PopplerDocument *document); -/* Interface for getting the Index of a poppler_document */ #define POPPLER_TYPE_INDEX_ITER (poppler_index_iter_get_type()) POPPLER_PUBLIC GType poppler_index_iter_get_type(void) G_GNUC_CONST; @@ -443,7 +442,6 @@ PopplerAction *poppler_index_iter_get_action(PopplerIndexIter *iter); POPPLER_PUBLIC gboolean poppler_index_iter_next(PopplerIndexIter *iter); -/* Interface for getting the Fonts of a poppler_document */ #define POPPLER_TYPE_FONT_INFO (poppler_font_info_get_type()) #define POPPLER_FONT_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_FONT_INFO, PopplerFontInfo)) #define POPPLER_IS_FONT_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_FONT_INFO)) @@ -482,7 +480,6 @@ gboolean poppler_fonts_iter_is_subset(PopplerFontsIter *iter); POPPLER_PUBLIC gboolean poppler_fonts_iter_next(PopplerFontsIter *iter); -/* Interface for getting the Layers of a poppler_document */ #define POPPLER_TYPE_LAYERS_ITER (poppler_layers_iter_get_type()) POPPLER_PUBLIC GType poppler_layers_iter_get_type(void) G_GNUC_CONST; @@ -502,7 +499,6 @@ PopplerLayer *poppler_layers_iter_get_layer(PopplerLayersIter *iter); POPPLER_PUBLIC gboolean poppler_layers_iter_next(PopplerLayersIter *iter); -/* Export to ps */ #define POPPLER_TYPE_PS_FILE (poppler_ps_file_get_type()) #define POPPLER_PS_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), POPPLER_TYPE_PS_FILE, PopplerPSFile)) #define POPPLER_IS_PS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), POPPLER_TYPE_PS_FILE)) @@ -521,6 +517,11 @@ void poppler_ps_file_set_duplex(PopplerPSFile *ps_file, gboolean duplex); POPPLER_PUBLIC void poppler_ps_file_free(PopplerPSFile *ps_file); +POPPLER_PUBLIC +void poppler_document_sign(PopplerDocument *document, const PopplerSigningData *signing_data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +POPPLER_PUBLIC +gboolean poppler_document_sign_finish(PopplerDocument *document, GAsyncResult *result, GError **error); + /** * PopplerPageRange: * @start_page: first page in the range of pages @@ -538,4 +539,10 @@ struct _PopplerPageRange G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerIndexIter, poppler_index_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerFontInfo, poppler_font_info_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerFontsIter, poppler_fonts_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerLayersIter, poppler_layers_iter_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerPSFile, poppler_ps_file_free) + #endif /* __POPPLER_DOCUMENT_H__ */ diff --git a/glib/poppler-form-field.cc b/glib/poppler-form-field.cc index 314b2cf..1aa2921 100644 --- a/glib/poppler-form-field.cc +++ b/glib/poppler-form-field.cc @@ -4,7 +4,9 @@ * Copyright (C) 2006 Julien Rebetez * Copyright (C) 2020 Oliver Sander * Copyright (C) 2021 André Guerreiro - * Copyright (C) 2021 Marek Kasik + * Copyright (C) 2021, 2023 Marek Kasik + * Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela + * Copyright (C) 2025 Jan-Michael Brummer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +28,12 @@ #include "poppler.h" #include "poppler-private.h" +#include +#ifdef ENABLE_NSS3 +# include +#endif +#include + /** * SECTION:poppler-form-field * @short_description: Form Field @@ -355,11 +363,9 @@ gchar *poppler_form_field_get_mapping_name(PopplerFormField *field) **/ gchar *poppler_form_field_get_name(PopplerFormField *field) { - GooString *tmp; - g_return_val_if_fail(POPPLER_IS_FORM_FIELD(field), NULL); - tmp = field->widget->getFullyQualifiedName(); + const GooString *tmp = field->widget->getFullyQualifiedName(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } @@ -387,12 +393,40 @@ gchar *poppler_form_field_get_alternate_ui_name(PopplerFormField *field) return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } +/** + * PopplerCertificateInfo: + * + * PopplerCertificateInfo contains detailed info about a signing certificate. + * + * Since 24.10 this type supports g_autoptr + * + * Since: 23.07.0 + */ +struct _PopplerCertificateInfo +{ + char *id; + char *subject_common_name; + char *subject_organization; + char *subject_email; + char *issuer_common_name; + char *issuer_organization; + char *issuer_email; + GDateTime *issued; + GDateTime *expires; +}; + +typedef struct _PopplerCertificateInfo PopplerCertificateInfo; + +G_DEFINE_BOXED_TYPE(PopplerCertificateInfo, poppler_certificate_info, poppler_certificate_info_copy, poppler_certificate_info_free) + /** * PopplerSignatureInfo: * * PopplerSignatureInfo contains detailed info about a signature * contained in a form field. * + * Since 24.10 this type supports g_autoptr + * * Since: 21.12.0 **/ struct _PopplerSignatureInfo @@ -401,6 +435,7 @@ struct _PopplerSignatureInfo PopplerCertificateStatus cert_status; char *signer_name; GDateTime *local_signing_time; + PopplerCertificateInfo *certificate_info; }; static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormField *field, PopplerSignatureValidationFlags flags, gboolean force_revalidation, GError **error) @@ -408,6 +443,7 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF FormFieldSignature *sig_field; SignatureInfo *sig_info; PopplerSignatureInfo *poppler_sig_info; + const X509CertificateInfo *certificate_info; if (poppler_form_field_get_field_type(field) != POPPLER_FORM_FIELD_SIGNATURE) { g_set_error(error, POPPLER_ERROR, POPPLER_ERROR_INVALID, "Wrong FormField type"); @@ -416,8 +452,9 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF sig_field = static_cast(field->widget->getField()); - sig_info = sig_field->validateSignature(flags & POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE, force_revalidation, -1, flags & POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK, - flags & POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH); + sig_info = sig_field->validateSignatureAsync(flags & POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE, force_revalidation, -1, flags & POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK, + flags & POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH, {}); + CertificateValidationStatus certificateStatus = sig_field->validateSignatureResult(); poppler_sig_info = g_new0(PopplerSignatureInfo, 1); switch (sig_info->getSignatureValStatus()) { @@ -444,7 +481,7 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF break; } - switch (sig_info->getCertificateValStatus()) { + switch (certificateStatus) { case CERTIFICATE_TRUSTED: poppler_sig_info->cert_status = POPPLER_CERTIFICATE_TRUSTED; break; @@ -468,9 +505,27 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF break; } - poppler_sig_info->signer_name = g_strdup(sig_info->getSignerName()); + std::string signerName = sig_info->getSignerName(); + poppler_sig_info->signer_name = g_strdup(signerName.c_str()); poppler_sig_info->local_signing_time = g_date_time_new_from_unix_local(sig_info->getSigningTime()); + certificate_info = sig_info->getCertificateInfo(); + if (certificate_info != nullptr) { + const X509CertificateInfo::EntityInfo &subject_info = certificate_info->getSubjectInfo(); + const X509CertificateInfo::EntityInfo &issuer_info = certificate_info->getIssuerInfo(); + const X509CertificateInfo::Validity &validity = certificate_info->getValidity(); + + poppler_sig_info->certificate_info = poppler_certificate_info_new(); + poppler_sig_info->certificate_info->subject_common_name = g_strdup(subject_info.commonName.c_str()); + poppler_sig_info->certificate_info->subject_organization = g_strdup(subject_info.organization.c_str()); + poppler_sig_info->certificate_info->subject_email = g_strdup(subject_info.email.c_str()); + poppler_sig_info->certificate_info->issuer_common_name = g_strdup(issuer_info.commonName.c_str()); + poppler_sig_info->certificate_info->issuer_email = g_strdup(issuer_info.email.c_str()); + poppler_sig_info->certificate_info->issuer_organization = g_strdup(issuer_info.organization.c_str()); + poppler_sig_info->certificate_info->issued = g_date_time_new_from_unix_utc(validity.notBefore); + poppler_sig_info->certificate_info->expires = g_date_time_new_from_unix_utc(validity.notAfter); + } + return poppler_sig_info; } @@ -511,7 +566,7 @@ PopplerSignatureInfo *poppler_form_field_signature_validate_sync(PopplerFormFiel PopplerSignatureInfo *signature_info; GTask *task; - g_return_val_if_fail(error == NULL || *error == NULL, NULL); + g_return_val_if_fail(error == nullptr || *error == nullptr, NULL); task = g_task_new(field, cancellable, nullptr, nullptr); g_task_set_task_data(task, GINT_TO_POINTER(flags), nullptr); @@ -530,8 +585,8 @@ PopplerSignatureInfo *poppler_form_field_signature_validate_sync(PopplerFormFiel * @field: a #PopplerFormField that represents a signature annotation * @flags: #PopplerSignatureValidationFlags flags influencing process of validation of the field signature * @cancellable: (nullable): optional #GCancellable object - * @callback: (scope async): a #GAsyncReadyCallback to call when the signature is validated - * @user_data: (closure): the data to pass to callback function + * @callback: (scope async) (closure user_data): a #GAsyncReadyCallback to call when the signature is validated + * @user_data: the data to pass to callback function * * Asynchronously validates the cryptographic signature contained in @signature_field. * @@ -587,13 +642,14 @@ PopplerSignatureInfo *poppler_signature_info_copy(const PopplerSignatureInfo *si { PopplerSignatureInfo *new_info; - g_return_val_if_fail(siginfo != NULL, NULL); + g_return_val_if_fail(siginfo != nullptr, NULL); new_info = g_new(PopplerSignatureInfo, 1); new_info->sig_status = siginfo->sig_status; new_info->cert_status = siginfo->cert_status; new_info->signer_name = g_strdup(siginfo->signer_name); new_info->local_signing_time = g_date_time_ref(siginfo->local_signing_time); + new_info->certificate_info = poppler_certificate_info_copy(siginfo->certificate_info); return new_info; } @@ -614,6 +670,7 @@ void poppler_signature_info_free(PopplerSignatureInfo *siginfo) g_date_time_unref(siginfo->local_signing_time); g_free(siginfo->signer_name); + poppler_certificate_info_free(siginfo->certificate_info); g_free(siginfo); } @@ -629,11 +686,28 @@ void poppler_signature_info_free(PopplerSignatureInfo *siginfo) **/ PopplerSignatureStatus poppler_signature_info_get_signature_status(const PopplerSignatureInfo *siginfo) { - g_return_val_if_fail(siginfo != NULL, POPPLER_SIGNATURE_GENERIC_ERROR); + g_return_val_if_fail(siginfo != nullptr, POPPLER_SIGNATURE_GENERIC_ERROR); return siginfo->sig_status; } +/** + * poppler_signature_info_get_certificate_info: + * @siginfo: a #PopplerSignatureInfo + * + * Returns PopplerCertificateInfo for given PopplerSignatureInfo. + * + * Return value: (transfer none): certificate info of the signature + * + * Since: 23.08.0 + **/ +PopplerCertificateInfo *poppler_signature_info_get_certificate_info(const PopplerSignatureInfo *siginfo) +{ + g_return_val_if_fail(siginfo != nullptr, NULL); + + return siginfo->certificate_info; +} + /** * poppler_signature_info_get_certificate_status: * @siginfo: a #PopplerSignatureInfo @@ -646,7 +720,7 @@ PopplerSignatureStatus poppler_signature_info_get_signature_status(const Poppler **/ PopplerCertificateStatus poppler_signature_info_get_certificate_status(const PopplerSignatureInfo *siginfo) { - g_return_val_if_fail(siginfo != NULL, POPPLER_CERTIFICATE_GENERIC_ERROR); + g_return_val_if_fail(siginfo != nullptr, POPPLER_CERTIFICATE_GENERIC_ERROR); return siginfo->cert_status; } @@ -663,7 +737,7 @@ PopplerCertificateStatus poppler_signature_info_get_certificate_status(const Pop **/ const gchar *poppler_signature_info_get_signer_name(const PopplerSignatureInfo *siginfo) { - g_return_val_if_fail(siginfo != NULL, NULL); + g_return_val_if_fail(siginfo != nullptr, NULL); return siginfo->signer_name; } @@ -684,7 +758,7 @@ const gchar *poppler_signature_info_get_signer_name(const PopplerSignatureInfo * **/ GDateTime *poppler_signature_info_get_local_signing_time(const PopplerSignatureInfo *siginfo) { - g_return_val_if_fail(siginfo != NULL, NULL); + g_return_val_if_fail(siginfo != nullptr, NULL); return siginfo->local_signing_time; } @@ -745,17 +819,15 @@ gchar *poppler_form_field_text_get_text(PopplerFormField *field) **/ void poppler_form_field_text_set_text(PopplerFormField *field, const gchar *text) { - GooString *goo_tmp; gchar *tmp; gsize length = 0; g_return_if_fail(field->widget->getType() == formText); tmp = text ? g_convert(text, -1, "UTF-16BE", "UTF-8", nullptr, &length, nullptr) : nullptr; - goo_tmp = new GooString(tmp, length); + std::unique_ptr goo_tmp = std::make_unique(tmp, length); g_free(tmp); - static_cast(field->widget)->setContent(goo_tmp); - delete goo_tmp; + static_cast(field->widget)->setContent(std::move(goo_tmp)); } /** @@ -1001,17 +1073,15 @@ void poppler_form_field_choice_toggle_item(PopplerFormField *field, gint index) **/ void poppler_form_field_choice_set_text(PopplerFormField *field, const gchar *text) { - GooString *goo_tmp; gchar *tmp; gsize length = 0; g_return_if_fail(field->widget->getType() == formChoice); tmp = text ? g_convert(text, -1, "UTF-16BE", "UTF-8", nullptr, &length, nullptr) : nullptr; - goo_tmp = new GooString(tmp, length); + std::unique_ptr goo_tmp = std::make_unique(tmp, length); g_free(tmp); - static_cast(field->widget)->setEditChoice(goo_tmp); - delete goo_tmp; + static_cast(field->widget)->setEditChoice(std::move(goo_tmp)); } /** @@ -1031,3 +1101,1219 @@ gchar *poppler_form_field_choice_get_text(PopplerFormField *field) tmp = static_cast(field->widget)->getEditChoice(); return tmp ? _poppler_goo_string_to_utf8(tmp) : nullptr; } + +/** + * PopplerSigningData: + * + * Since 24.10 this type supports g_autoptr + */ +struct _PopplerSigningData +{ + char *destination_filename; + PopplerCertificateInfo *certificate_info; + int page; + + char *signature_text; + char *signature_text_left; + PopplerRectangle signature_rect; + + PopplerColor font_color; + gdouble font_size; + gdouble left_font_size; + + PopplerColor border_color; + gdouble border_width; + + PopplerColor background_color; + + char *field_partial_name; + char *reason; + char *location; + char *image_path; + char *password; + char *document_owner_password; + char *document_user_password; +}; + +typedef struct _PopplerSigningData PopplerSigningData; + +G_DEFINE_BOXED_TYPE(PopplerSigningData, poppler_signing_data, poppler_signing_data_copy, poppler_signing_data_free) + +/** + * poppler_signing_data_new: + * + * Creates a new #PopplerSigningData with default content. + * + * Return value: a new #PopplerSigningData. It must be freed with poppler_signing_data_free() when done. + * + * Since: 23.07.0 + **/ +PopplerSigningData *poppler_signing_data_new(void) +{ + PopplerSigningData *data = (PopplerSigningData *)g_malloc0(sizeof(PopplerSigningData)); + + data->password = g_strdup(""); + data->page = 0; + + data->font_size = 10.0; + data->left_font_size = 20.0; + data->border_width = 1.5; + + /* Grey background */ + auto background_color = PopplerColor(); + background_color.red = 0xEF00; + background_color.green = 0xEF00; + background_color.blue = 0xEF00; + poppler_signing_data_set_background_color(data, &background_color); + + /* Red border color */ + auto border_color = PopplerColor(); + border_color.red = 0xFFFF; + border_color.green = 0x00; + border_color.blue = 0x00; + poppler_signing_data_set_border_color(data, &border_color); + + /* Red font color */ + auto font_color = PopplerColor(); + font_color.red = 0xFFFF; + font_color.green = 0x00; + border_color.blue = 0x00; + poppler_signing_data_set_font_color(data, &font_color); + + return data; +} + +/** + * poppler_signing_data_copy: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Copies @signing_data, creating an identical #PopplerSigningData. + * + * Return value: (transfer full): a new #PopplerSigningData structure identical to @signing_data + * + * Since: 23.07.0 + **/ +PopplerSigningData *poppler_signing_data_copy(const PopplerSigningData *signing_data) +{ + PopplerSigningData *data; + + g_return_val_if_fail(signing_data != nullptr, nullptr); + + data = (PopplerSigningData *)g_malloc0(sizeof(PopplerSigningData)); + data->destination_filename = g_strdup(signing_data->destination_filename); + data->certificate_info = poppler_certificate_info_copy(signing_data->certificate_info); + data->page = signing_data->page; + + data->signature_text = g_strdup(signing_data->signature_text); + data->signature_text_left = g_strdup(signing_data->signature_text_left); + memcpy(&data->signature_rect, &signing_data->signature_rect, sizeof(PopplerRectangle)); + + memcpy(&data->font_color, &signing_data->font_color, sizeof(PopplerColor)); + data->font_size = signing_data->font_size; + data->left_font_size = signing_data->left_font_size; + + memcpy(&data->border_color, &signing_data->border_color, sizeof(PopplerColor)); + data->border_width = signing_data->border_width; + + memcpy(&data->background_color, &signing_data->background_color, sizeof(PopplerColor)); + + data->field_partial_name = g_strdup(signing_data->field_partial_name); + data->reason = g_strdup(signing_data->reason); + data->location = g_strdup(signing_data->location); + data->image_path = g_strdup(signing_data->image_path); + data->password = g_strdup(signing_data->password); + data->document_owner_password = g_strdup(signing_data->document_owner_password); + data->document_user_password = g_strdup(signing_data->document_user_password); + + return data; +} + +/** + * poppler_signing_data_free: + * @signing_data: (nullable): a #PopplerSigningData structure containing signing data + * + * Frees @signing_data + * + * Since: 23.07.0 + **/ +void poppler_signing_data_free(PopplerSigningData *signing_data) +{ + if (!signing_data) { + return; + } + + g_clear_pointer(&signing_data->destination_filename, g_free); + g_clear_pointer(&signing_data->certificate_info, poppler_certificate_info_free); + g_clear_pointer(&signing_data->signature_text, g_free); + g_clear_pointer(&signing_data->signature_text_left, g_free); + g_clear_pointer(&signing_data->field_partial_name, g_free); + g_clear_pointer(&signing_data->reason, g_free); + g_clear_pointer(&signing_data->location, g_free); + g_clear_pointer(&signing_data->image_path, g_free); + + if (signing_data->password) { +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(signing_data->password, strlen(signing_data->password)); +#else + memset(signing_data->password, 0, strlen(signing_data->password)); +#endif + g_clear_pointer(&signing_data->password, g_free); + } + + if (signing_data->document_owner_password) { +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(signing_data->document_owner_password, strlen(signing_data->document_owner_password)); +#else + memset(signing_data->document_owner_password, 0, strlen(signing_data->document_owner_password)); +#endif + g_clear_pointer(&signing_data->document_owner_password, g_free); + } + + if (signing_data->document_user_password) { +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(signing_data->document_user_password, strlen(signing_data->document_user_password)); +#else + memset(signing_data->document_user_password, 0, strlen(signing_data->document_user_password)); +#endif + g_clear_pointer(&signing_data->document_user_password, g_free); + } + + g_free(signing_data); +} + +/** + * poppler_signing_data_set_destination_filename: + * @signing_data: a #PopplerSigningData structure containing signing data + * @filename: destination filename + * + * Set destination file name. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_destination_filename(PopplerSigningData *signing_data, const gchar *filename) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(filename != nullptr); + + if (signing_data->destination_filename == filename) { + return; + } + + g_clear_pointer(&signing_data->destination_filename, g_free); + signing_data->destination_filename = g_strdup(filename); +} + +/** + * poppler_signing_data_get_destination_filename: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get destination file name. + * + * Return value: destination filename + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_destination_filename(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + + return signing_data->destination_filename; +} + +/** + * poppler_signing_data_set_certificate_info: + * @signing_data: a #PopplerSigningData structure containing signing data + * @certificate_info: a #PopplerCertificateInfo + * + * Set certification information. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_certificate_info(PopplerSigningData *signing_data, const PopplerCertificateInfo *certificate_info) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(certificate_info != nullptr); + + if (signing_data->certificate_info == certificate_info) { + return; + } + + g_clear_pointer(&signing_data->certificate_info, poppler_certificate_info_free); + signing_data->certificate_info = poppler_certificate_info_copy(certificate_info); +} + +/** + * poppler_signing_data_get_certificate_info: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get certification information. + * + * Return value: a #PopplerCertificateInfo + * + * Since: 23.07.0 + **/ +const PopplerCertificateInfo *poppler_signing_data_get_certificate_info(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->certificate_info; +} + +/** + * poppler_signing_data_set_page: + * @signing_data: a #PopplerSigningData structure containing signing data + * @page: a page number + * + * Set page (>=0). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_page(PopplerSigningData *signing_data, int page) +{ + g_return_if_fail(signing_data != nullptr); + + if (page < 0) { + return; + } + + signing_data->page = page; +} + +/** + * poppler_signing_data_get_page: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get page. + * + * Return value: page number + * + * Since: 23.07.0 + **/ +int poppler_signing_data_get_page(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, 0); + return signing_data->page; +} + +/** + * poppler_signing_data_set_signature_text: + * @signing_data: a #PopplerSigningData structure containing signing data + * @signature_text: text to show as main signature + * + * Set signature text. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_signature_text(PopplerSigningData *signing_data, const gchar *signature_text) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(signature_text != nullptr); + + if (signing_data->signature_text == signature_text) { + return; + } + + g_clear_pointer(&signing_data->signature_text, g_free); + signing_data->signature_text = g_strdup(signature_text); +} + +/** + * poppler_signing_data_get_signature_text: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature text. + * + * Return value: signature text + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_signature_text(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->signature_text; +} + +/** + * poppler_signing_data_set_signature_text_left: + * @signing_data: a #PopplerSigningData structure containing signing data + * @signature_text_left: text to show as small left signature + * + * Set small signature text on the left hand. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_signature_text_left(PopplerSigningData *signing_data, const gchar *signature_text_left) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(signature_text_left != nullptr); + + if (signing_data->signature_text_left == signature_text_left) { + return; + } + + g_clear_pointer(&signing_data->signature_text_left, g_free); + signing_data->signature_text_left = g_strdup(signature_text_left); +} + +/** + * poppler_signing_data_get_signature_text_left: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature text left. + * + * Return value: signature text left + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_signature_text_left(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->signature_text_left; +} + +/** + * poppler_signing_data_set_signature_rectangle: + * @signing_data: a #PopplerSigningData structure containing signing data + * @signature_rect: a #PopplerRectangle where signature should be shown + * + * Set signature rectangle. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_signature_rectangle(PopplerSigningData *signing_data, const PopplerRectangle *signature_rect) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(signature_rect != nullptr); + + memcpy(&signing_data->signature_rect, signature_rect, sizeof(PopplerRectangle)); +} + +/** + * poppler_signing_data_get_signature_rectangle: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature rectangle. + * + * Return value: a #PopplerRectangle + * + * Since: 23.07.0 + **/ +const PopplerRectangle *poppler_signing_data_get_signature_rectangle(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return &signing_data->signature_rect; +} + +/** + * poppler_signing_data_set_font_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * @font_color: a #PopplerColor to be used as signature font color + * + * Set signature font color. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_font_color(PopplerSigningData *signing_data, const PopplerColor *font_color) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(font_color != nullptr); + + memcpy(&signing_data->font_color, font_color, sizeof(PopplerColor)); +} + +/** + * poppler_signing_data_get_font_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature font color. + * + * Return value: a #PopplerColor + * + * Since: 23.07.0 + **/ +const PopplerColor *poppler_signing_data_get_font_color(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return &signing_data->font_color; +} + +/** + * poppler_signing_data_set_font_size: + * @signing_data: a #PopplerSigningData structure containing signing data + * @font_size: signature font size + * + * Set signature font size (>0). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_font_size(PopplerSigningData *signing_data, gdouble font_size) +{ + g_return_if_fail(signing_data != nullptr); + + if (font_size <= 0) { + return; + } + + signing_data->font_size = font_size; +} + +/** + * poppler_signing_data_get_font_size: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature font size. + * + * Return value: font size + * + * Since: 23.07.0 + **/ +gdouble poppler_signing_data_get_font_size(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, 20.0f); + return signing_data->font_size; +} + +/** + * poppler_signing_data_set_left_font_size: + * @signing_data: a #PopplerSigningData structure containing signing data + * @font_size: signature font size + * + * Set signature left font size (> 0). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_left_font_size(PopplerSigningData *signing_data, gdouble left_font_size) +{ + g_return_if_fail(signing_data != nullptr); + + if (left_font_size <= 0) { + return; + } + + signing_data->left_font_size = left_font_size; +} + +/** + * poppler_signing_data_get_left_font_size: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature left font size. + * + * Return value: left font size + * + * Since: 23.07.0 + **/ +gdouble poppler_signing_data_get_left_font_size(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, 12.0); + return signing_data->left_font_size; +} + +/** + * poppler_signing_data_set_border_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * @border_color: a #PopplerColor to be used for signature border + * + * Set signature border color. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_border_color(PopplerSigningData *signing_data, const PopplerColor *border_color) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(border_color != nullptr); + + memcpy(&signing_data->border_color, border_color, sizeof(PopplerColor)); +} + +/** + * poppler_signing_data_get_border_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature border color. + * + * Return value: a #PopplerColor + * + * Since: 23.07.0 + **/ +const PopplerColor *poppler_signing_data_get_border_color(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return &signing_data->border_color; +} + +/** + * poppler_signing_data_set_border_width: + * @signing_data: a #PopplerSigningData structure containing signing data + * @border_width: border width + * + * Set signature border width. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_border_width(PopplerSigningData *signing_data, gdouble border_width) +{ + g_return_if_fail(signing_data != nullptr); + + if (border_width < 0) { + return; + } + + signing_data->border_width = border_width; +} + +/** + * poppler_signing_data_get_border_width: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature border width. + * + * Return value: border width + * + * Since: 23.07.0 + **/ +gdouble poppler_signing_data_get_border_width(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, 12); + return signing_data->border_width; +} + +/** + * poppler_signing_data_set_background_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * @background_color: a #PopplerColor to be used for signature background + * + * Set signature background color. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_background_color(PopplerSigningData *signing_data, const PopplerColor *background_color) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(background_color != nullptr); + + memcpy(&signing_data->background_color, background_color, sizeof(PopplerColor)); +} + +/** + * poppler_signing_data_get_background_color: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signature background color. + * + * Return value: a #PopplerColor + * + * Since: 23.07.0 + **/ +const PopplerColor *poppler_signing_data_get_background_color(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return &signing_data->background_color; +} + +/** + * poppler_signing_data_set_field_partial_name: + * @signing_data: a #PopplerSigningData structure containing signing data + * @field_partial_name: a field partial name + * + * Set field partial name (existing field id or a new one) where signature is placed. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_field_partial_name(PopplerSigningData *signing_data, const gchar *field_partial_name) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(field_partial_name != nullptr); + + g_clear_pointer(&signing_data->field_partial_name, g_free); + signing_data->field_partial_name = g_strdup(field_partial_name); +} + +/** + * poppler_signing_data_get_field_partial_name: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get field partial name. + * + * Return value: field partial name + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_field_partial_name(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, ""); + return signing_data->field_partial_name; +} + +/** + * poppler_signing_data_set_reason: + * @signing_data: a #PopplerSigningData structure containing signing data + * @reason: a reason + * + * Set reason for signature (e.g. I'm approver). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_reason(PopplerSigningData *signing_data, const gchar *reason) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(reason != nullptr); + + if (signing_data->reason == reason) { + return; + } + + g_clear_pointer(&signing_data->reason, g_free); + signing_data->reason = g_strdup(reason); +} + +/** + * poppler_signing_data_get_reason: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get reason. + * + * Return value: reason + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_reason(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->reason; +} + +/** + * poppler_signing_data_set_location: + * @signing_data: a #PopplerSigningData structure containing signing data + * @location: a location + * + * Set signature location (e.g. "At my desk"). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_location(PopplerSigningData *signing_data, const gchar *location) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(location != nullptr); + + if (signing_data->location == location) { + return; + } + + g_clear_pointer(&signing_data->location, g_free); + signing_data->location = g_strdup(location); +} + +/** + * poppler_signing_data_get_location: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get location. + * + * Return value: location + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_location(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->location; +} + +/** + * poppler_signing_data_set_image_path: + * @signing_data: a #PopplerSigningData structure containing signing data + * @image_path: signature image path + * + * Set signature background (watermark) image path. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_image_path(PopplerSigningData *signing_data, const gchar *image_path) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(image_path != nullptr); + + if (signing_data->image_path == image_path) { + return; + } + + g_clear_pointer(&signing_data->image_path, g_free); + signing_data->image_path = g_strdup(image_path); +} + +/** + * poppler_signing_data_get_image_path: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get image path. + * + * Return value: image path + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_image_path(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->image_path; +} + +/** + * poppler_signing_data_set_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * @password: a password + * + * Set password for the signing key. + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_password(PopplerSigningData *signing_data, const gchar *password) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(password != nullptr); + + if (signing_data->password == password) { + return; + } + + g_clear_pointer(&signing_data->password, g_free); + signing_data->password = g_strdup(password); +} + +/** + * poppler_signing_data_get_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get signing key password. + * + * Return value: password + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_password(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->password; +} + +/** + * poppler_signing_data_set_document_owner_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * @document_owner_password: document owner password + * + * Set document owner password (for encrypted files). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_document_owner_password(PopplerSigningData *signing_data, const gchar *document_owner_password) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(document_owner_password != nullptr); + + if (signing_data->document_owner_password == document_owner_password) { + return; + } + + g_clear_pointer(&signing_data->document_owner_password, g_free); + signing_data->document_owner_password = g_strdup(document_owner_password); +} + +/** + * poppler_signing_data_get_document_owner_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get document owner password. + * + * Return value: document owner password (for encrypted files) + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_document_owner_password(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, nullptr); + return signing_data->document_owner_password; +} + +/** + * poppler_signing_data_set_document_user_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * @document_user_password: document user password + * + * Set document user password (for encrypted files). + * + * Since: 23.07.0 + **/ +void poppler_signing_data_set_document_user_password(PopplerSigningData *signing_data, const gchar *document_user_password) +{ + g_return_if_fail(signing_data != nullptr); + g_return_if_fail(document_user_password != nullptr); + + if (signing_data->document_user_password == document_user_password) { + return; + } + + g_clear_pointer(&signing_data->document_user_password, g_free); + signing_data->document_user_password = g_strdup(document_user_password); +} + +/** + * poppler_signing_data_get_document_user_password: + * @signing_data: a #PopplerSigningData structure containing signing data + * + * Get document user password. + * + * Return value: document user password (for encrypted files) + * + * Since: 23.07.0 + **/ +const gchar *poppler_signing_data_get_document_user_password(const PopplerSigningData *signing_data) +{ + g_return_val_if_fail(signing_data != nullptr, ""); + return signing_data->document_user_password; +} + +/* Certificate Information */ + +/** + * poppler_certificate_info_new: + * + * Creates a new #PopplerCertificateInfo + * + * Return value: a new #PopplerCertificateInfo. It must be freed with poppler_certificate_info_free() when done. + * + * Since: 23.07.0 + **/ +PopplerCertificateInfo *poppler_certificate_info_new(void) +{ + return (PopplerCertificateInfo *)g_malloc0(sizeof(PopplerCertificateInfo)); +} + +/** + * poppler_certificate_info_get_id: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate nick name + * + * Return value: certificate nick name + * + * Since: 23.07.0 + **/ +const char *poppler_certificate_info_get_id(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->id; +} + +/** + * poppler_certificate_info_get_subject_common_name: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate subject common name + * + * Return value: certificate subject common name + * + * Since: 23.07.0 + **/ +const char *poppler_certificate_info_get_subject_common_name(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->subject_common_name; +} + +/** + * poppler_certificate_info_get_subject_organization: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate subject organization + * + * Return value: certificate subject organization + * + * Since: 23.08.0 + **/ +const char *poppler_certificate_info_get_subject_organization(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->subject_organization; +} + +/** + * poppler_certificate_info_get_subject_email: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate subject email + * + * Return value: certificate subject email + * + * Since: 23.08.0 + **/ +const char *poppler_certificate_info_get_subject_email(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->subject_email; +} + +/** + * poppler_certificate_info_get_issuer_common_name: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate issuer common name + * + * Return value: certificate issuer common name + * + * Since: 23.08.0 + **/ +const char *poppler_certificate_info_get_issuer_common_name(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->issuer_common_name; +} + +/** + * poppler_certificate_info_get_issuer_organization: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate issuer organization + * + * Return value: certificate issuer organization + * + * Since: 23.08.0 + **/ +const char *poppler_certificate_info_get_issuer_organization(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->issuer_organization; +} + +/** + * poppler_certificate_info_get_issuer_email: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate issuer email + * + * Return value: certificate issuer email + * + * Since: 23.08.0 + **/ +const char *poppler_certificate_info_get_issuer_email(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->issuer_email; +} + +/** + * poppler_certificate_info_get_issuance_time: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate issuance time + * + * Return value: (transfer none): certificate issuance time + * + * Since: 23.08.0 + **/ +GDateTime *poppler_certificate_info_get_issuance_time(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->issued; +} + +/** + * poppler_certificate_info_get_expiration_time: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Get certificate expiration time + * + * Return value: (transfer none): certificate expiration time + * + * Since: 23.08.0 + **/ +GDateTime *poppler_certificate_info_get_expiration_time(const PopplerCertificateInfo *certificate_info) +{ + g_return_val_if_fail(certificate_info != nullptr, nullptr); + return certificate_info->expires; +} + +static PopplerCertificateInfo *create_certificate_info(const X509CertificateInfo *ci) +{ + PopplerCertificateInfo *certificate_info; + + g_return_val_if_fail(ci != nullptr, nullptr); + + const X509CertificateInfo::EntityInfo &subject_info = ci->getSubjectInfo(); + const X509CertificateInfo::EntityInfo &issuer_info = ci->getIssuerInfo(); + const X509CertificateInfo::Validity &validity = ci->getValidity(); + + certificate_info = poppler_certificate_info_new(); + certificate_info->id = g_strdup(ci->getNickName().c_str()); + certificate_info->subject_common_name = g_strdup(subject_info.commonName.c_str()); + certificate_info->subject_organization = g_strdup(subject_info.organization.c_str()); + certificate_info->subject_email = g_strdup(subject_info.email.c_str()); + certificate_info->issuer_common_name = g_strdup(issuer_info.commonName.c_str()); + certificate_info->issuer_organization = g_strdup(issuer_info.organization.c_str()); + certificate_info->issuer_email = g_strdup(issuer_info.email.c_str()); + certificate_info->issued = g_date_time_new_from_unix_utc(validity.notBefore); + certificate_info->expires = g_date_time_new_from_unix_utc(validity.notAfter); + + return certificate_info; +} + +/** + * poppler_certificate_info_copy: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Copies @certificate_info, creating an identical #PopplerCertificateInfo. + * + * Return value: (transfer full): a new #PopplerCertificateInfo structure identical to @certificate_info + * + * Since: 23.07.0 + **/ +PopplerCertificateInfo *poppler_certificate_info_copy(const PopplerCertificateInfo *certificate_info) +{ + PopplerCertificateInfo *dup; + + g_return_val_if_fail(certificate_info != nullptr, nullptr); + + dup = (PopplerCertificateInfo *)g_malloc0(sizeof(PopplerCertificateInfo)); + dup->id = g_strdup(certificate_info->id); + dup->subject_common_name = g_strdup(certificate_info->subject_common_name); + dup->subject_organization = g_strdup(certificate_info->subject_organization); + dup->subject_email = g_strdup(certificate_info->subject_email); + dup->issuer_common_name = g_strdup(certificate_info->issuer_common_name); + dup->issuer_organization = g_strdup(certificate_info->issuer_organization); + dup->issuer_email = g_strdup(certificate_info->issuer_email); + dup->issued = g_date_time_ref(certificate_info->issued); + dup->expires = g_date_time_ref(certificate_info->expires); + + return dup; +} + +/** + * poppler_certificate_info_free: + * @certificate_info: a #PopplerCertificateInfo structure containing certificate information + * + * Frees @certificate_info + * + * Since: 23.07.0 + **/ +void poppler_certificate_info_free(PopplerCertificateInfo *certificate_info) +{ + if (certificate_info == nullptr) { + return; + } + + g_clear_pointer(&certificate_info->id, g_free); + g_clear_pointer(&certificate_info->subject_common_name, g_free); + g_clear_pointer(&certificate_info->subject_organization, g_free); + g_clear_pointer(&certificate_info->subject_email, g_free); + g_clear_pointer(&certificate_info->issuer_common_name, g_free); + g_clear_pointer(&certificate_info->issuer_organization, g_free); + g_clear_pointer(&certificate_info->issuer_email, g_free); + g_clear_pointer(&certificate_info->issued, g_date_time_unref); + g_clear_pointer(&certificate_info->expires, g_date_time_unref); + + g_free(certificate_info); +} + +/** + * poppler_get_available_signing_certificates: + * + * Get all available signing certificate information + * + * Returns: (transfer full) (element-type PopplerCertificateInfo): all available signing certificate information + **/ +GList *poppler_get_available_signing_certificates(void) +{ + GList *list = nullptr; + auto backend = CryptoSign::Factory::createActive(); + + if (!backend) { + return nullptr; + } + + std::vector> vCerts = backend->getAvailableSigningCertificates(); + for (auto &cert : vCerts) { + PopplerCertificateInfo *certificate_info = create_certificate_info(cert.get()); + list = g_list_append(list, certificate_info); + } + return list; +} + +/** + * poppler_get_certificate_info_by_id: + * + * Get certificate by nick name + * + * Returns: (transfer full): a #PopplerCertificateInfo or %NULL if not found + **/ +PopplerCertificateInfo *poppler_get_certificate_info_by_id(const char *id) +{ + PopplerCertificateInfo *ret = nullptr; + GList *certificate_info = poppler_get_available_signing_certificates(); + GList *list; + + for (list = certificate_info; list != nullptr; list = list->next) { + PopplerCertificateInfo *info = (PopplerCertificateInfo *)list->data; + + if (g_strcmp0(info->id, id) == 0) { + ret = poppler_certificate_info_copy(info); + break; + } + } + + g_list_free_full(certificate_info, (GDestroyNotify)poppler_certificate_info_free); + + return ret; +} + +/* NSS functions */ + +/** + * poppler_set_nss_dir: + * + * Set NSS directory + * + * Since: 23.07.0 + **/ +void poppler_set_nss_dir(const char *path) +{ +#ifdef ENABLE_NSS3 + NSSSignatureConfiguration::setNSSDir(GooString(path)); +#else + (void)path; +#endif +} + +/** + * poppler_get_nss_dir: + * + * Get NSS directory + * + * Return value: (transfer full): nss directroy. + * + * Since: 23.07.0 + **/ +char *poppler_get_nss_dir(void) +{ +#ifdef ENABLE_NSS3 + return g_strdup(NSSSignatureConfiguration::getNSSDir().c_str()); +#else + return nullptr; +#endif +} + +/** + * poppler_set_nss_password_callback: + * @func: (scope call): a #PopplerNssPasswordFunc that represents a signature annotation + * + * A callback which asks for certificate password + * + * Since: 23.07.0 + **/ +void poppler_set_nss_password_callback(PopplerNssPasswordFunc func) +{ +#ifdef ENABLE_NSS3 + NSSSignatureConfiguration::setNSSPasswordCallback(func); +#else + g_warning("poppler_set_nss_password_callback called but this poppler is built without NSS support"); + (void)func; +#endif +} diff --git a/glib/poppler-form-field.h b/glib/poppler-form-field.h index b48c506..35eb03f 100644 --- a/glib/poppler-form-field.h +++ b/glib/poppler-form-field.h @@ -2,7 +2,7 @@ * * Copyright (C) 2007 Carlos Garcia Campos * Copyright (C) 2021 André Guerreiro - * Copyright (C) 2021 Marek Kasik + * Copyright (C) 2021, 2023 Marek Kasik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -245,10 +245,144 @@ PopplerSignatureStatus poppler_signature_info_get_signature_status(const Poppler POPPLER_PUBLIC PopplerCertificateStatus poppler_signature_info_get_certificate_status(const PopplerSignatureInfo *siginfo); POPPLER_PUBLIC +PopplerCertificateInfo *poppler_signature_info_get_certificate_info(const PopplerSignatureInfo *siginfo); +POPPLER_PUBLIC const gchar *poppler_signature_info_get_signer_name(const PopplerSignatureInfo *siginfo); POPPLER_PUBLIC GDateTime *poppler_signature_info_get_local_signing_time(const PopplerSignatureInfo *siginfo); +/* Signing Data */ +#define POPPLER_TYPE_SIGNING_DATA (poppler_signing_data_get_type()) +POPPLER_PUBLIC +GType poppler_signing_data_get_type(void) G_GNUC_CONST; +POPPLER_PUBLIC +PopplerSigningData *poppler_signing_data_new(void); +POPPLER_PUBLIC +PopplerSigningData *poppler_signing_data_copy(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_free(PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_destination_filename(PopplerSigningData *signing_data, const gchar *filename); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_destination_filename(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_certificate_info(PopplerSigningData *signing_data, const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const PopplerCertificateInfo *poppler_signing_data_get_certificate_info(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_page(PopplerSigningData *signing_data, int page); +POPPLER_PUBLIC +int poppler_signing_data_get_page(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_signature_text(PopplerSigningData *signing_data, const gchar *signature_text); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_signature_text(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_signature_text_left(PopplerSigningData *signing_data, const gchar *signature_text_left); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_signature_text_left(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_signature_rectangle(PopplerSigningData *signing_data, const PopplerRectangle *signature_rect); +POPPLER_PUBLIC +const PopplerRectangle *poppler_signing_data_get_signature_rectangle(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_font_color(PopplerSigningData *signing_data, const PopplerColor *font_color); +POPPLER_PUBLIC +const PopplerColor *poppler_signing_data_get_font_color(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_font_size(PopplerSigningData *signing_data, gdouble font_size); +POPPLER_PUBLIC +gdouble poppler_signing_data_get_font_size(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_left_font_size(PopplerSigningData *signing_data, gdouble font_size); +POPPLER_PUBLIC +gdouble poppler_signing_data_get_left_font_size(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_border_color(PopplerSigningData *signing_data, const PopplerColor *border_color); +POPPLER_PUBLIC +const PopplerColor *poppler_signing_data_get_border_color(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_border_width(PopplerSigningData *signing_data, gdouble border_width); +POPPLER_PUBLIC +gdouble poppler_signing_data_get_border_width(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_background_color(PopplerSigningData *signing_data, const PopplerColor *background_color); +POPPLER_PUBLIC +const PopplerColor *poppler_signing_data_get_background_color(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_field_partial_name(PopplerSigningData *signing_data, const gchar *field_partial_name); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_field_partial_name(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_reason(PopplerSigningData *signing_data, const gchar *reason); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_reason(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_location(PopplerSigningData *signing_data, const gchar *location); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_location(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_image_path(PopplerSigningData *signing_data, const gchar *image_path); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_image_path(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_password(PopplerSigningData *signing_data, const gchar *password); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_password(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_document_owner_password(PopplerSigningData *signing_data, const gchar *document_owner_password); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_document_owner_password(const PopplerSigningData *signing_data); +POPPLER_PUBLIC +void poppler_signing_data_set_document_user_password(PopplerSigningData *signing_data, const gchar *document_user_password); +POPPLER_PUBLIC +const gchar *poppler_signing_data_get_document_user_password(const PopplerSigningData *signing_data); + +/* Certificate Information */ +#define POPPLER_TYPE_CERTIFICATE_INFO (poppler_certificate_info_get_type()) +POPPLER_PUBLIC +GType poppler_certificate_info_get_type(void) G_GNUC_CONST; +PopplerCertificateInfo *poppler_certificate_info_new(void); +POPPLER_PUBLIC +PopplerCertificateInfo *poppler_certificate_info_copy(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +void poppler_certificate_info_free(PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_id(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_subject_common_name(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_subject_organization(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_subject_email(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_issuer_common_name(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_issuer_organization(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +const char *poppler_certificate_info_get_issuer_email(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +GDateTime *poppler_certificate_info_get_issuance_time(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +GDateTime *poppler_certificate_info_get_expiration_time(const PopplerCertificateInfo *certificate_info); +POPPLER_PUBLIC +PopplerCertificateInfo *poppler_get_certificate_info_by_id(const char *id); +POPPLER_PUBLIC +GList *poppler_get_available_signing_certificates(void); + +/* NSS */ +POPPLER_PUBLIC +void poppler_set_nss_dir(const char *path); +POPPLER_PUBLIC +char *poppler_get_nss_dir(void); +typedef char *(*PopplerNssPasswordFunc)(const gchar *text); +POPPLER_PUBLIC +void poppler_set_nss_password_callback(PopplerNssPasswordFunc func); + G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerSignatureInfo, poppler_signature_info_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerSigningData, poppler_signing_data_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerCertificateInfo, poppler_certificate_info_free) + #endif /* __POPPLER_FORM_FIELD_H__ */ diff --git a/glib/poppler-media.cc b/glib/poppler-media.cc index c4f8d24..d9d52b7 100644 --- a/glib/poppler-media.cc +++ b/glib/poppler-media.cc @@ -1,6 +1,7 @@ /* poppler-media.cc: glib interface to MediaRendition * * Copyright (C) 2010 Carlos Garcia Campos + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -356,7 +357,9 @@ gboolean poppler_media_save_to_callback(PopplerMedia *poppler_media, PopplerMedi g_return_val_if_fail(poppler_media->stream.isStream(), FALSE); stream = poppler_media->stream.getStream(); - stream->reset(); + if (!stream->reset()) { + return FALSE; + } do { int data; diff --git a/glib/poppler-page.cc b/glib/poppler-page.cc index 7f47cd5..d62893d 100644 --- a/glib/poppler-page.cc +++ b/glib/poppler-page.cc @@ -255,67 +255,57 @@ static TextPage *poppler_page_get_text_page(PopplerPage *page) { if (page->text == nullptr) { TextOutputDev *text_dev; - Gfx *gfx; text_dev = new TextOutputDev(nullptr, true, 0, false, false); - gfx = page->page->createGfx(text_dev, 72.0, 72.0, 0, false, /* useMediaBox */ - true, /* Crop */ - -1, -1, -1, -1, false, /* printing */ - nullptr, nullptr); - page->page->display(gfx); + std::unique_ptr gfx = page->page->createGfx(text_dev, 72.0, 72.0, 0, false, /* useMediaBox */ + true, /* Crop */ + -1, -1, -1, -1, nullptr, nullptr); + page->page->display(gfx.get()); text_dev->endPage(); page->text = text_dev->takeText(); - delete gfx; + gfx.reset(); // deletion order here is important, gfx before text_dev delete text_dev; } return page->text; } -static gboolean annot_is_markup(Annot *annot) +static bool annots_display_decide_cb(Annot *annot, void *user_data) { - switch (annot->getType()) { - case Annot::typeLink: - case Annot::typePopup: - case Annot::typeMovie: - case Annot::typeScreen: - case Annot::typePrinterMark: - case Annot::typeTrapNet: - case Annot::typeWatermark: - case Annot::type3D: - case Annot::typeWidget: - return FALSE; - default: - return TRUE; - } -} - -static bool poppler_print_annot_cb(Annot *annot, void *user_data) -{ - PopplerPrintFlags user_print_flags = (PopplerPrintFlags)GPOINTER_TO_INT(user_data); - - if (annot->getFlags() & Annot::flagHidden) { - return false; - } - - if (user_print_flags & POPPLER_PRINT_STAMP_ANNOTS_ONLY) { - return (annot->getType() == Annot::typeStamp) ? (annot->getFlags() & Annot::flagPrint) : (annot->getType() == Annot::typeWidget); - } + PopplerRenderAnnotsFlags flags = (PopplerRenderAnnotsFlags)GPOINTER_TO_UINT(user_data); + Annot::AnnotSubtype type = annot->getType(); + int typeMask = 1 << MAX(0, (((int)type) - 1)); - if (user_print_flags & POPPLER_PRINT_MARKUP_ANNOTS) { - return annot_is_markup(annot) ? (annot->getFlags() & Annot::flagPrint) : (annot->getType() == Annot::typeWidget); + if (flags & typeMask) { + return true; } - - /* Print document only, form fields are always printed */ - return (annot->getType() == Annot::typeWidget); + return false; } -static void _poppler_page_render(PopplerPage *page, cairo_t *cairo, bool printing, PopplerPrintFlags print_flags) +/** + * poppler_page_render_full: + * @page: the page to render from + * @cairo: cairo context to render to + * @printing: cairo context to render to + * @flags: flags which allow to select which annotations to render + * + * Render the page to the given cairo context, manually selecting which + * annotations should be displayed. + * + * The @printing parameter determines whether a page is rendered for printing + * or for displaying it on a screen. See the documentation for + * poppler_page_render_for_printing() for the differences between rendering to + * the screen and rendering to a printer. + * + * Since: 25.02 + **/ +void poppler_page_render_full(PopplerPage *page, cairo_t *cairo, gboolean printing, PopplerRenderAnnotsFlags flags) { CairoOutputDev *output_dev; g_return_if_fail(POPPLER_IS_PAGE(page)); + g_return_if_fail(cairo != nullptr); output_dev = page->document->output_dev; output_dev->setCairo(cairo); @@ -325,12 +315,13 @@ static void _poppler_page_render(PopplerPage *page, cairo_t *cairo, bool printin page->text = new TextPage(false); output_dev->setTextPage(page->text); } - /* NOTE: instead of passing -1 we should/could use cairo_clip_extents() - * to get a bounding box */ + cairo_save(cairo); page->page->displaySlice(output_dev, 72.0, 72.0, 0, false, /* useMediaBox */ true, /* Crop */ - -1, -1, -1, -1, printing, nullptr, nullptr, printing ? poppler_print_annot_cb : nullptr, printing ? GINT_TO_POINTER((gint)print_flags) : nullptr); + -1, -1, -1, -1, /* instead of passing -1 we could use cairo_clip_extents() to get a bounding box */ + + printing, nullptr, nullptr, annots_display_decide_cb, GUINT_TO_POINTER((guint)flags)); cairo_restore(cairo); output_dev->setCairo(nullptr); @@ -351,9 +342,7 @@ static void _poppler_page_render(PopplerPage *page, cairo_t *cairo, bool printin **/ void poppler_page_render(PopplerPage *page, cairo_t *cairo) { - g_return_if_fail(POPPLER_IS_PAGE(page)); - - _poppler_page_render(page, cairo, false, (PopplerPrintFlags)0); + poppler_page_render_full(page, cairo, false, POPPLER_RENDER_ANNOTS_ALL); } /** @@ -369,13 +358,24 @@ void poppler_page_render(PopplerPage *page, cairo_t *cairo) * differences between rendering to the screen and rendering to a printer. * * Since: 0.16 + * + * Deprecated: 25.02: Use poppler_page_render_full() instead. **/ +G_GNUC_BEGIN_IGNORE_DEPRECATIONS void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options) { - g_return_if_fail(POPPLER_IS_PAGE(page)); + int flags = (int)POPPLER_RENDER_ANNOTS_PRINT_DOCUMENT; + + if (options & POPPLER_PRINT_STAMP_ANNOTS_ONLY) { + flags |= POPPLER_RENDER_ANNOTS_PRINT_STAMP; + } + if (options & POPPLER_PRINT_MARKUP_ANNOTS) { + flags |= POPPLER_RENDER_ANNOTS_PRINT_MARKUP; + } - _poppler_page_render(page, cairo, true, options); + poppler_page_render_full(page, cairo, true, (PopplerRenderAnnotsFlags)flags); } +G_GNUC_END_IGNORE_DEPRECATIONS /** * poppler_page_render_for_printing: @@ -384,7 +384,8 @@ void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *c * * Render the page to the given cairo context for printing with * #POPPLER_PRINT_ALL flags selected. If you want a different set of flags, - * use poppler_page_render_for_printing_with_options(). + * use poppler_page_render_full() with printing #TRUE and the corresponding + * flags. * * The difference between poppler_page_render() and this function is that some * things get rendered differently between screens and printers: @@ -415,9 +416,7 @@ void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *c **/ void poppler_page_render_for_printing(PopplerPage *page, cairo_t *cairo) { - g_return_if_fail(POPPLER_IS_PAGE(page)); - - _poppler_page_render(page, cairo, true, POPPLER_PRINT_ALL); + poppler_page_render_full(page, cairo, true, POPPLER_RENDER_ANNOTS_PRINT_ALL); } static cairo_surface_t *create_surface_from_thumbnail_data(guchar *data, gint width, gint height, gint rowstride) @@ -726,13 +725,13 @@ cairo_region_t *poppler_page_get_selected_region(PopplerPage *page, gdouble scal * * Retrieves the contents of the specified @selection as text. * - * Return value: a pointer to the contents of the @selection - * as a string + * Returns: (transfer full): a pointer to the contents of the + * @selection as a string + * * Since: 0.16 **/ char *poppler_page_get_selected_text(PopplerPage *page, PopplerSelectionStyle style, PopplerRectangle *selection) { - GooString *sel_text; char *result; TextPage *text; SelectionStyle selection_style = selectionStyleGlyph; @@ -759,9 +758,8 @@ char *poppler_page_get_selected_text(PopplerPage *page, PopplerSelectionStyle st } text = poppler_page_get_text_page(page); - sel_text = text->getSelectionText(&pdf_selection, selection_style); - result = g_strdup(sel_text->c_str()); - delete sel_text; + GooString sel_text = text->getSelectionText(&pdf_selection, selection_style); + result = g_strdup(sel_text.c_str()); return result; } @@ -924,7 +922,6 @@ GList *poppler_page_find_text(PopplerPage *page, const char *text) static CairoImageOutputDev *poppler_page_get_image_output_dev(PopplerPage *page, bool (*imgDrawDeviceCbk)(int img_id, void *data), void *imgDrawCbkData) { CairoImageOutputDev *image_dev; - Gfx *gfx; image_dev = new CairoImageOutputDev(); @@ -932,12 +929,10 @@ static CairoImageOutputDev *poppler_page_get_image_output_dev(PopplerPage *page, image_dev->setImageDrawDecideCbk(imgDrawDeviceCbk, imgDrawCbkData); } - gfx = page->page->createGfx(image_dev, 72.0, 72.0, 0, false, /* useMediaBox */ - true, /* Crop */ - -1, -1, -1, -1, false, /* printing */ - nullptr, nullptr); - page->page->display(gfx); - delete gfx; + std::unique_ptr gfx = page->page->createGfx(image_dev, 72.0, 72.0, 0, false, /* useMediaBox */ + true, /* Crop */ + -1, -1, -1, -1, nullptr, nullptr); + page->page->display(gfx.get()); return image_dev; } @@ -1579,22 +1574,24 @@ void poppler_page_add_annot(PopplerPage *page, PopplerAnnot *annot) AnnotTextMarkup *annot_markup = dynamic_cast(annot->annot); if (annot_markup) { - AnnotQuadrilaterals *quads; crop_box = _poppler_annot_get_cropbox(annot); if (crop_box) { /* Handle hypothetical case of annot being added is already existing on a prior page, so * first remove cropbox of the prior page before adding cropbox of the new page later */ - quads = new_quads_from_offset_cropbox(crop_box, annot_markup->getQuadrilaterals(), FALSE); - annot_markup->setQuadrilaterals(quads); + AnnotQuadrilaterals *quads = new_quads_from_offset_cropbox(crop_box, annot_markup->getQuadrilaterals(), FALSE); + annot_markup->setQuadrilaterals(*quads); + delete quads; } if (page_is_rotated) { /* Quadrilateral's coords need to be saved un-rotated (same as rect coords) */ - quads = _page_new_quads_unrotated(page->page, annot_markup->getQuadrilaterals()); - annot_markup->setQuadrilaterals(quads); + AnnotQuadrilaterals *quads = _page_new_quads_unrotated(page->page, annot_markup->getQuadrilaterals()); + annot_markup->setQuadrilaterals(*quads); + delete quads; } /* Add to annot's quadrilaterals the offset for the cropbox of the new page */ - quads = new_quads_from_offset_cropbox(page_crop_box, annot_markup->getQuadrilaterals(), TRUE); - annot_markup->setQuadrilaterals(quads); + AnnotQuadrilaterals *quads = new_quads_from_offset_cropbox(page_crop_box, annot_markup->getQuadrilaterals(), TRUE); + annot_markup->setQuadrilaterals(*quads); + delete quads; } page->page->addAnnot(annot->annot); @@ -2334,12 +2331,10 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang TextPage *text; PopplerRectangle *rect; PDFRectangle selection; - int i, k; guint offset = 0; guint n_rects = 0; - gdouble x1, y1, x2, y2; + gdouble x1 = 0, y1 = 0, x2 = 0, y2 = 0; gdouble x3, y3, x4, y4; - int n_lines; g_return_val_if_fail(POPPLER_IS_PAGE(page), FALSE); g_return_val_if_fail(area != nullptr, FALSE); @@ -2352,19 +2347,18 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang selection.y2 = area->y2; text = poppler_page_get_text_page(page); - std::vector **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); - if (!word_list) { + std::vector>> word_list = text->getSelectionWords(&selection, selectionStyleGlyph); + if (word_list.empty()) { return FALSE; } - n_rects += n_lines - 1; - for (i = 0; i < n_lines; i++) { - std::vector *line_words = word_list[i]; - n_rects += line_words->size() - 1; - for (std::size_t j = 0; j < line_words->size(); j++) { - const TextWordSelection *word_sel = (*line_words)[j]; + n_rects += word_list.size() - 1; + for (const std::vector> &line_words : word_list) { + n_rects += line_words.size() - 1; + for (std::size_t j = 0; j < line_words.size(); j++) { + const TextWordSelection *word_sel = line_words[j].get(); n_rects += word_sel->getEnd() - word_sel->getBegin(); - if (!word_sel->getWord()->hasSpaceAfter() && j < line_words->size() - 1) { + if (!word_sel->getWord()->hasSpaceAfter() && j < line_words.size() - 1) { n_rects--; } } @@ -2373,14 +2367,14 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang *rectangles = g_new(PopplerRectangle, n_rects); *n_rectangles = n_rects; - for (i = 0; i < n_lines; i++) { - std::vector *line_words = word_list[i]; - for (std::size_t j = 0; j < line_words->size(); j++) { - TextWordSelection *word_sel = (*line_words)[j]; + for (size_t i = 0; i < word_list.size(); i++) { + std::vector> &line_words = word_list[i]; + for (std::size_t j = 0; j < line_words.size(); j++) { + TextWordSelection *word_sel = line_words[j].get(); const TextWord *word = word_sel->getWord(); int end = word_sel->getEnd(); - for (k = word_sel->getBegin(); k < end; k++) { + for (int k = word_sel->getBegin(); k < end; k++) { rect = *rectangles + offset; word->getCharBBox(k, &(rect->x1), &(rect->y1), &(rect->x2), &(rect->y2)); offset++; @@ -2389,8 +2383,8 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang rect = *rectangles + offset; word->getBBox(&x1, &y1, &x2, &y2); - if (word->hasSpaceAfter() && j < line_words->size() - 1) { - TextWordSelection *next_word_sel = (*line_words)[j + 1]; + if (word->hasSpaceAfter() && j < line_words.size() - 1) { + TextWordSelection *next_word_sel = line_words[j + 1].get(); next_word_sel->getWord()->getBBox(&x3, &y3, &x4, &y4); // space is from one word to other and with the same height as @@ -2401,11 +2395,9 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang rect->y2 = y2; offset++; } - - delete word_sel; } - if (i < n_lines - 1 && offset > 0) { + if (i < word_list.size() - 1 && offset > 0) { // end of line rect->x1 = x2; rect->y1 = y2; @@ -2413,12 +2405,7 @@ gboolean poppler_page_get_text_layout_for_area(PopplerPage *page, PopplerRectang rect->y2 = y2; offset++; } - - delete line_words; } - - gfree(word_list); - return TRUE; } @@ -2510,11 +2497,9 @@ GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRecta { TextPage *text; PDFRectangle selection; - int n_lines; PopplerTextAttributes *attrs = nullptr; const TextWord *word, *prev_word = nullptr; gint word_i, prev_word_i; - gint i; gint offset = 0; GList *attributes = nullptr; @@ -2527,15 +2512,15 @@ GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRecta selection.y2 = area->y2; text = poppler_page_get_text_page(page); - std::vector **word_list = text->getSelectionWords(&selection, selectionStyleGlyph, &n_lines); - if (!word_list) { + std::vector>> word_list = text->getSelectionWords(&selection, selectionStyleGlyph); + if (word_list.empty()) { return nullptr; } - for (i = 0; i < n_lines; i++) { - std::vector *line_words = word_list[i]; - for (std::size_t j = 0; j < line_words->size(); j++) { - TextWordSelection *word_sel = (*line_words)[j]; + for (size_t i = 0; i < word_list.size(); i++) { + std::vector> &line_words = word_list[i]; + for (std::size_t j = 0; j < line_words.size(); j++) { + TextWordSelection *word_sel = line_words[j].get(); int end = word_sel->getEnd(); word = word_sel->getWord(); @@ -2552,23 +2537,16 @@ GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRecta prev_word_i = word_i; } - if (word->hasSpaceAfter() && j < line_words->size() - 1) { + if (word->hasSpaceAfter() && j < line_words.size() - 1) { attrs->end_index = offset; offset++; } - - delete word_sel; } - if (i < n_lines - 1) { + if (i < word_list.size() - 1) { attrs->end_index = offset; offset++; } - - delete line_words; } - - gfree(word_list); - return g_list_reverse(attributes); } diff --git a/glib/poppler-page.h b/glib/poppler-page.h index 2d037d8..d6872bd 100644 --- a/glib/poppler-page.h +++ b/glib/poppler-page.h @@ -37,9 +37,13 @@ GType poppler_page_get_type(void) G_GNUC_CONST; POPPLER_PUBLIC void poppler_page_render(PopplerPage *page, cairo_t *cairo); POPPLER_PUBLIC +void poppler_page_render_full(PopplerPage *page, cairo_t *cairo, gboolean printing, PopplerRenderAnnotsFlags flags); +POPPLER_PUBLIC void poppler_page_render_for_printing(PopplerPage *page, cairo_t *cairo); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS POPPLER_PUBLIC -void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options); +void poppler_page_render_for_printing_with_options(PopplerPage *page, cairo_t *cairo, PopplerPrintFlags options) G_GNUC_DEPRECATED_FOR(poppler_page_render_full); +G_GNUC_END_IGNORE_DEPRECATIONS POPPLER_PUBLIC cairo_surface_t *poppler_page_get_thumbnail(PopplerPage *page); POPPLER_PUBLIC @@ -123,6 +127,8 @@ GList *poppler_page_get_text_attributes_for_area(PopplerPage *page, PopplerRecta * * A #PopplerRectangle is used to describe * locations on a page and bounding boxes + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerRectangle { @@ -153,6 +159,8 @@ gboolean poppler_rectangle_find_get_ignored_hyphen(const PopplerRectangle *recta * @y: y coordinate * * A #PopplerPoint is used to describe a location point on a page + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerPoint { @@ -187,6 +195,8 @@ void poppler_point_free(PopplerPoint *point); * A #PopplerQuadrilateral is used to describe rectangle-like polygon * with arbitrary inclination on a page. * + * Since 24.10 this type supports g_autoptr + * * Since: 0.26 **/ struct _PopplerQuadrilateral @@ -217,6 +227,8 @@ void poppler_quadrilateral_free(PopplerQuadrilateral *quad); * * A #PopplerColor describes a RGB color. Color components * are values between 0 and 65535 + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerColor { @@ -248,6 +260,8 @@ void poppler_color_free(PopplerColor *color); * A #PopplerTextAttributes is used to describe text attributes of a range of text * * Since: 0.18 + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerTextAttributes { @@ -279,6 +293,8 @@ void poppler_text_attributes_free(PopplerTextAttributes *text_attrs); * * A #PopplerLinkMapping structure represents the location * of @action on the page + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerLinkMapping { @@ -319,6 +335,8 @@ void poppler_link_mapping_free(PopplerLinkMapping *mapping); * * A #PopplerPageTransition structures describes a visual transition * to use when moving between pages during a presentation + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerPageTransition { @@ -351,6 +369,8 @@ void poppler_page_transition_free(PopplerPageTransition *transition); * * A #PopplerImageMapping structure represents the location * of an image on the page + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerImageMapping { @@ -377,6 +397,8 @@ void poppler_image_mapping_free(PopplerImageMapping *mapping); * * A #PopplerFormFieldMapping structure represents the location * of @field on the page + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerFormFieldMapping { @@ -403,6 +425,8 @@ void poppler_form_field_mapping_free(PopplerFormFieldMapping *mapping); * * A #PopplerAnnotMapping structure represents the location * of @annot on the page + * + * Since 24.10 this type supports g_autoptr */ struct _PopplerAnnotMapping { @@ -421,4 +445,15 @@ void poppler_annot_mapping_free(PopplerAnnotMapping *mapping); G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerRectangle, poppler_rectangle_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerPoint, poppler_point_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerQuadrilateral, poppler_quadrilateral_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerColor, poppler_color_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerTextAttributes, poppler_text_attributes_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerLinkMapping, poppler_link_mapping_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerPageTransition, poppler_page_transition_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerImageMapping, poppler_image_mapping_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerFormFieldMapping, poppler_form_field_mapping_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerAnnotMapping, poppler_annot_mapping_free) + #endif /* __POPPLER_PAGE_H__ */ diff --git a/glib/poppler-private.h b/glib/poppler-private.h index 758c702..41101de 100644 --- a/glib/poppler-private.h +++ b/glib/poppler-private.h @@ -21,7 +21,7 @@ # include #endif -#define SUPPORTED_ROTATION(r) (r == 90 || r == 180 || r == 270) +#define SUPPORTED_ROTATION(r) ((r) == 90 || (r) == 180 || (r) == 270) struct _PopplerDocument { @@ -163,8 +163,10 @@ const PDFRectangle *_poppler_annot_get_cropbox(PopplerAnnot *poppler_annot); char *_poppler_goo_string_to_utf8(const GooString *s); gboolean _poppler_convert_pdf_date_to_gtime(const GooString *date, time_t *gdate); GDateTime *_poppler_convert_pdf_date_to_date_time(const GooString *date); -GooString *_poppler_convert_date_time_to_pdf_date(GDateTime *datetime); +std::unique_ptr _poppler_convert_date_time_to_pdf_date(GDateTime *datetime); AnnotStampImageHelper *_poppler_convert_cairo_image_to_stamp_image_helper(const cairo_surface_t *image); +PopplerColor *_poppler_convert_annot_color_to_poppler_color(const AnnotColor *color); +std::unique_ptr _poppler_convert_poppler_color_to_annot_color(const PopplerColor *poppler_color); void _poppler_error_cb(ErrorCategory category, Goffset pos, const char *message); diff --git a/glib/poppler-structure-element.cc b/glib/poppler-structure-element.cc index 4c6cd02..8511ede 100644 --- a/glib/poppler-structure-element.cc +++ b/glib/poppler-structure-element.cc @@ -1,7 +1,8 @@ /* poppler-structure.cc: glib interface to poppler * * Copyright (C) 2013 Igalia S.L. - * Copyright (C) 2018 Albert Astals Cid + * Copyright (C) 2018, 2025 Albert Astals Cid + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -573,6 +574,11 @@ gchar *poppler_structure_element_get_text(PopplerStructureElement *poppler_struc return result; } +/** + * PopplerStructureElementIter: + * + * Since 24.10 this type supports g_autoptr + */ struct _PopplerStructureElementIter { PopplerDocument *document; @@ -761,6 +767,11 @@ PopplerStructureElementIter *poppler_structure_element_iter_get_child(PopplerStr return nullptr; } +/** + * PopplerTextSpan: + * + * Since 24.10 this type supports g_autoptr + */ struct _PopplerTextSpan { gchar *text; @@ -782,7 +793,7 @@ enum static PopplerTextSpan *text_span_poppler_text_span(const TextSpan &span) { PopplerTextSpan *new_span = g_slice_new0(PopplerTextSpan); - if (GooString *text = span.getText()) { + if (const GooString *text = span.getText()) { new_span->text = _poppler_goo_string_to_utf8(text); } @@ -796,11 +807,14 @@ static PopplerTextSpan *text_span_poppler_text_span(const TextSpan &span) const GooString *font_name = span.getFont()->getFamily(); if (font_name) { new_span->font_name = _poppler_goo_string_to_utf8(font_name); - } else if (span.getFont()->getName()) { - const GooString aux(*span.getFont()->getName()); - new_span->font_name = _poppler_goo_string_to_utf8(&aux); } else { - new_span->font_name = nullptr; + const std::optional &fontName = span.getFont()->getName(); + if (fontName) { + const GooString aux(*fontName); + new_span->font_name = _poppler_goo_string_to_utf8(&aux); + } else { + new_span->font_name = nullptr; + } } if (span.getFont()->isFixedWidth()) { diff --git a/glib/poppler-structure-element.h b/glib/poppler-structure-element.h index 05cef08..649e22b 100644 --- a/glib/poppler-structure-element.h +++ b/glib/poppler-structure-element.h @@ -422,4 +422,7 @@ const gchar *poppler_text_span_get_font_name(PopplerTextSpan *poppler_text_span) G_END_DECLS +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerTextSpan, poppler_text_span_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(PopplerStructureElementIter, poppler_structure_element_iter_free) + #endif /* !__POPPLER_STRUCTURE_ELEMENT_H__ */ diff --git a/glib/poppler.h b/glib/poppler.h index 710b7b2..ed5edcc 100644 --- a/glib/poppler.h +++ b/glib/poppler.h @@ -47,7 +47,8 @@ typedef enum POPPLER_ERROR_ENCRYPTED, POPPLER_ERROR_OPEN_FILE, POPPLER_ERROR_BAD_CATALOG, - POPPLER_ERROR_DAMAGED + POPPLER_ERROR_DAMAGED, + POPPLER_ERROR_SIGNING } PopplerError; /** @@ -130,6 +131,91 @@ typedef enum POPPLER_SELECTION_LINE } PopplerSelectionStyle; +/** + * PopplerRenderFlags: + * @POPPLER_RENDER_ANNOTS_NONE: do not render annotations + * @POPPLER_RENDER_ANNOTS_TEXT: render text annotations + * @POPPLER_RENDER_ANNOTS_LINK: render link annotations + * @POPPLER_RENDER_ANNOTS_FREETEXT: render freetext annotations, + * @POPPLER_RENDER_ANNOTS_LINE: render line annotations, + * @POPPLER_RENDER_ANNOTS_SQUARE: render square annotations, + * @POPPLER_RENDER_ANNOTS_CIRCLE: render circle annotations, + * @POPPLER_RENDER_ANNOTS_POLYGON: render polygon annotations, + * @POPPLER_RENDER_ANNOTS_POLYLINE: render polyline annotations, + * @POPPLER_RENDER_ANNOTS_HIGHLIGHT: render highlight annotations, + * @POPPLER_RENDER_ANNOTS_UNDERLINE: render underline annotations, + * @POPPLER_RENDER_ANNOTS_SQUIGGLY: render squiggly annotations, + * @POPPLER_RENDER_ANNOTS_STRIKEOUT: render strikeout annotations, + * @POPPLER_RENDER_ANNOTS_STAMP: render stamp annotations, + * @POPPLER_RENDER_ANNOTS_CARET: render caret annotations, + * @POPPLER_RENDER_ANNOTS_INK: render ink annotations, + * @POPPLER_RENDER_ANNOTS_POPUP: render popup annotations, + * @POPPLER_RENDER_ANNOTS_FILEATTACHMENT: render fileattachment annotations, + * @POPPLER_RENDER_ANNOTS_SOUND: render sound annotations, + * @POPPLER_RENDER_ANNOTS_MOVIE: render movie annotations, + * @POPPLER_RENDER_ANNOTS_WIDGET: render widget annotations, + * @POPPLER_RENDER_ANNOTS_SCREEN: render screen annotations, + * @POPPLER_RENDER_ANNOTS_PRINTERMARK: render printermark annotations, + * @POPPLER_RENDER_ANNOTS_TRAPNET: render trapnet annotations, + * @POPPLER_RENDER_ANNOTS_WATERMARK: render watermark annotations, + * @POPPLER_RENDER_ANNOTS_3D: render 3D annotations, + * @POPPLER_RENDER_ANNOTS_RICHMEDIA: render richmedia annotations, + * @POPPLER_RENDER_ANNOTS_PRINT_DOCUMENT: render the default annotations used for printing + * @POPPLER_RENDER_ANNOTS_PRINT_MARKUP: render markup annotations and default annotations used for printing + * @POPPLER_RENDER_ANNOTS_PRINT_STAMP: render stamp annotations and default annotations used for printing + * @POPPLER_RENDER_ANNOTS_PRINT_ALL: render all possible annotations used for printing + * @POPPLER_RENDER_ANNOTS_ALL: render all annotations + * + * Flags to select which annotations to render. If the flag corresponding to a + * certain annotation type is on, then such annotation type will be rendered, + * when appropriate (e.g: won't be renderer if the annotation is not visible). + * This allows to combine multiple flags, like + * `POPPLER_RENDER_ANNOTS_LINK | POPPLER_RENDER_ANNOTS_TEXT`, or disable some + * specific annotations like + * `POPPLER_RENDER_ANNOTS_ALL & (~POPPLER_RENDER_ANNOTS_TEXT)` + * + * Since: 25.02 + */ +typedef enum /*< flags >*/ +{ + POPPLER_RENDER_ANNOTS_NONE = 0, + POPPLER_RENDER_ANNOTS_TEXT = 1 << 0, + POPPLER_RENDER_ANNOTS_LINK = 1 << 1, + POPPLER_RENDER_ANNOTS_FREETEXT = 1 << 2, + POPPLER_RENDER_ANNOTS_LINE = 1 << 3, + POPPLER_RENDER_ANNOTS_SQUARE = 1 << 4, + POPPLER_RENDER_ANNOTS_CIRCLE = 1 << 5, + POPPLER_RENDER_ANNOTS_POLYGON = 1 << 6, + POPPLER_RENDER_ANNOTS_POLYLINE = 1 << 7, + POPPLER_RENDER_ANNOTS_HIGHLIGHT = 1 << 8, + POPPLER_RENDER_ANNOTS_UNDERLINE = 1 << 9, + POPPLER_RENDER_ANNOTS_SQUIGGLY = 1 << 10, + POPPLER_RENDER_ANNOTS_STRIKEOUT = 1 << 11, + POPPLER_RENDER_ANNOTS_STAMP = 1 << 12, + POPPLER_RENDER_ANNOTS_CARET = 1 << 13, + POPPLER_RENDER_ANNOTS_INK = 1 << 14, + POPPLER_RENDER_ANNOTS_POPUP = 1 << 15, + POPPLER_RENDER_ANNOTS_FILEATTACHMENT = 1 << 16, + POPPLER_RENDER_ANNOTS_SOUND = 1 << 17, + POPPLER_RENDER_ANNOTS_MOVIE = 1 << 18, + POPPLER_RENDER_ANNOTS_WIDGET = 1 << 19, + POPPLER_RENDER_ANNOTS_SCREEN = 1 << 20, + POPPLER_RENDER_ANNOTS_PRINTERMARK = 1 << 21, + POPPLER_RENDER_ANNOTS_TRAPNET = 1 << 22, + POPPLER_RENDER_ANNOTS_WATERMARK = 1 << 23, + POPPLER_RENDER_ANNOTS_3D = 1 << 24, + POPPLER_RENDER_ANNOTS_RICHMEDIA = 1 << 25, + + /* Everything below are special flags to combine them all */ + POPPLER_RENDER_ANNOTS_PRINT_DOCUMENT = POPPLER_RENDER_ANNOTS_WIDGET, + POPPLER_RENDER_ANNOTS_PRINT_MARKUP = ~(POPPLER_RENDER_ANNOTS_LINK | POPPLER_RENDER_ANNOTS_POPUP | POPPLER_RENDER_ANNOTS_MOVIE | POPPLER_RENDER_ANNOTS_SCREEN | POPPLER_RENDER_ANNOTS_PRINTERMARK | POPPLER_RENDER_ANNOTS_TRAPNET + | POPPLER_RENDER_ANNOTS_WATERMARK | POPPLER_RENDER_ANNOTS_3D), + POPPLER_RENDER_ANNOTS_PRINT_STAMP = POPPLER_RENDER_ANNOTS_WIDGET | POPPLER_RENDER_ANNOTS_STAMP, + POPPLER_RENDER_ANNOTS_PRINT_ALL = POPPLER_RENDER_ANNOTS_PRINT_MARKUP, + /* Enable all flags, by shifting and substracting the last one */ + POPPLER_RENDER_ANNOTS_ALL = (POPPLER_RENDER_ANNOTS_RICHMEDIA << 1) - 1 +} PopplerRenderAnnotsFlags; + /** * PopplerPrintFlags: * @POPPLER_PRINT_DOCUMENT: print main document contents @@ -139,6 +225,9 @@ typedef enum * * Printing flags * + * Deprecated: 25.02: Use poppler_page_render_full() and + * #PopplerRenderAnnotsFlags instead. + * * Since: 0.16 */ typedef enum /*< flags >*/ @@ -147,7 +236,7 @@ typedef enum /*< flags >*/ POPPLER_PRINT_MARKUP_ANNOTS = 1 << 0, POPPLER_PRINT_STAMP_ANNOTS_ONLY = 1 << 1, POPPLER_PRINT_ALL = POPPLER_PRINT_MARKUP_ANNOTS -} PopplerPrintFlags; +} PopplerPrintFlags G_GNUC_DEPRECATED_FOR(PopplerRenderAnnotsFlags); /** * PopplerFindFlags: @@ -185,6 +274,7 @@ typedef struct _PopplerPoint PopplerPoint; typedef struct _PopplerRectangle PopplerRectangle; typedef struct _PopplerTextAttributes PopplerTextAttributes; typedef struct _PopplerColor PopplerColor; +typedef struct _PopplerFontDescription PopplerFontDescription; typedef struct _PopplerLinkMapping PopplerLinkMapping; typedef struct _PopplerPageTransition PopplerPageTransition; typedef struct _PopplerImageMapping PopplerImageMapping; @@ -220,6 +310,8 @@ typedef struct _PopplerTextSpan PopplerTextSpan; typedef struct _PopplerPageRange PopplerPageRange; typedef struct _PopplerSignatureInfo PopplerSignatureInfo; typedef struct _PopplerAnnotStamp PopplerAnnotStamp; +typedef struct _PopplerCertificateInfo PopplerCertificateInfo; +typedef struct _PopplerSigningData PopplerSigningData; /** * PopplerBackend: diff --git a/glib/reference/html/PopplerAction.html b/glib/reference/html/PopplerAction.html index 6575600..b2ca9cf 100644 --- a/glib/reference/html/PopplerAction.html +++ b/glib/reference/html/PopplerAction.html @@ -8,7 +8,7 @@ - + @@ -287,6 +287,7 @@

PopplerA

PopplerAction

A generic wrapper for actions that exposes only PopplerActionType.

+

Since 24.10 this type supports g_autoptr


@@ -316,6 +317,7 @@

PopplerA Also note that named_dest does not have a defined encoding and is not in a form suitable to be displayed to the user.

+

Since 24.10 this type supports g_autoptr

Members

@@ -361,22 +363,22 @@

PopplerA

- + - + - + - + @@ -408,7 +410,7 @@

PopplerA

- + @@ -442,7 +444,7 @@

PopplerA

- + @@ -482,12 +484,12 @@

PopplerA

- + - + @@ -527,17 +529,17 @@

PopplerA

- + - + - + @@ -571,7 +573,7 @@

PopplerA

- + @@ -610,12 +612,12 @@

PopplerA

- + - + @@ -650,7 +652,7 @@

PopplerA

- + @@ -696,12 +698,12 @@

PopplerA

- + - + @@ -730,7 +732,7 @@

PopplerA

The default behavior resets only the list of fields , but setting exclude - to TRUE will cause the action to reset all fields but those + to TRUE will cause the action to reset all fields but those listed. Providing an empty list of fields resets the entire form.

Members

@@ -747,18 +749,18 @@

PopplerA

- + - + - + @@ -793,12 +795,12 @@

PopplerA

- + - + @@ -833,12 +835,12 @@

PopplerA

- + - + @@ -1122,7 +1124,7 @@

PopplerA

- + @@ -1172,6 +1174,6 @@

PopplerA +
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/PopplerAttachment.html b/glib/reference/html/PopplerAttachment.html index b4d207d..933b570 100644 --- a/glib/reference/html/PopplerAttachment.html +++ b/glib/reference/html/PopplerAttachment.html @@ -8,7 +8,7 @@ - + @@ -42,14 +42,14 @@

Popp

- - - - + @@ -199,14 +199,14 @@

Popp

Returns

-

TRUE if successful, FALSE (with error +

TRUE if successful, FALSE (with error set) if failed.


poppler_attachment_get_checksum ()

-
const GString *
+
const GString *
 poppler_attachment_get_checksum (PopplerAttachment *attachment);

Parameters

@@ -232,7 +232,7 @@

Popp

poppler_attachment_get_ctime ()

-
GDateTime *
+
GDateTime *
 poppler_attachment_get_ctime (PopplerAttachment *attachment);

Parameters

@@ -252,7 +252,7 @@

Popp

Returns

The attachment's creation date and time -as a GDateTime, or NULL if the creation date and time is not available.

+as a GDateTime, or NULL if the creation date and time is not available.

[transfer none][nullable]

Since: 20.09.0

@@ -260,7 +260,7 @@

Popp

poppler_attachment_get_description ()

-
const gchar *
+
const gchar *
 poppler_attachment_get_description (PopplerAttachment *attachment);

Parameters

@@ -286,7 +286,7 @@

Popp

poppler_attachment_get_mtime ()

-
GDateTime *
+
GDateTime *
 poppler_attachment_get_mtime (PopplerAttachment *attachment);

Parameters

@@ -306,7 +306,7 @@

Popp

Returns

The attachment's modification date and -time as a GDateTime, or NULL if the modification date and time is not +time as a GDateTime, or NULL if the modification date and time is not available.

[transfer none][nullable]

@@ -315,7 +315,7 @@

Popp

poppler_attachment_get_name ()

-
const gchar *
+
const gchar *
 poppler_attachment_get_name (PopplerAttachment *attachment);

Parameters

@@ -341,7 +341,7 @@

Popp

poppler_attachment_get_size ()

-
gsize
+
gsize
 poppler_attachment_get_size (PopplerAttachment *attachment);

Parameters

@@ -367,15 +367,15 @@

Popp

poppler_attachment_save ()

-
gboolean
+
gboolean
 poppler_attachment_save (PopplerAttachment *attachment,
                          const char *filename,
-                         GError **error);
+ GError **error);

Saves attachment to a file indicated by filename . If error - is set, FALSE -will be returned. Possible errors include those in the G_FILE_ERROR domain + is set, FALSE +will be returned. Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates.

Parameters

@@ -398,7 +398,7 @@

Popp

- + @@ -406,21 +406,21 @@

Popp

Returns

-

TRUE, if the file successfully saved

+

TRUE, if the file successfully saved


poppler_attachment_save_to_fd ()

-
gboolean
+
gboolean
 poppler_attachment_save_to_fd (PopplerAttachment *attachment,
                                int fd,
-                               GError **error);
+ GError **error);

Saves attachment to a file referred to by fd . If error - is set, FALSE -will be returned. Possible errors include those in the G_FILE_ERROR domain + is set, FALSE +will be returned. Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates. Note that this function takes ownership of fd ; you must not operate on it @@ -446,7 +446,7 @@

Popp

- + @@ -454,25 +454,25 @@

Popp

Returns

-

TRUE, if the file successfully saved

+

TRUE, if the file successfully saved

Since: 21.12.0


poppler_attachment_save_to_callback ()

-
gboolean
+
gboolean
 poppler_attachment_save_to_callback (PopplerAttachment *attachment,
                                      PopplerAttachmentSaveFunc save_func,
-                                     gpointer user_data,
-                                     GError **error);
+ gpointer user_data, + GError **error);

Saves attachment by feeding the produced data to save_func . Can be used when you want to store the attachment to something other than a file, such as an in-memory buffer or a socket. If error - is set, FALSE will be -returned. Possible errors include those in the G_FILE_ERROR domain and + is set, FALSE will be +returned. Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates.

Parameters

@@ -500,7 +500,7 @@

Popp

- + @@ -508,7 +508,7 @@

Popp

Returns

-

TRUE, if the save successfully completed

+

TRUE, if the save successfully completed

@@ -538,37 +538,37 @@

Popp

- + - + - + - + - + - + @@ -580,6 +580,6 @@

Popp +
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/PopplerDocument.html b/glib/reference/html/PopplerDocument.html index 050297f..6f494ed 100644 --- a/glib/reference/html/PopplerDocument.html +++ b/glib/reference/html/PopplerDocument.html @@ -8,7 +8,7 @@ - + @@ -43,7 +43,7 @@

Popple

+GDateTime * @@ -806,12 +806,12 @@

Popple

- + - + @@ -822,7 +822,7 @@

Popple

- + @@ -839,7 +839,7 @@

Popple

+GDateTime * @@ -1017,7 +1017,7 @@

Popple GFlags ├── PopplerPermissions ╰── PopplerViewerPreferences - GObject + GObject ├── PopplerDocument ├── PopplerFontInfo ╰── PopplerPSFile @@ -1036,14 +1036,14 @@

Popple

Functions

poppler_document_create_dests_tree ()

-
GTree *
+
GTree *
 poppler_document_create_dests_tree (PopplerDocument *document);

Creates a balanced binary tree of all named destinations in document

The tree key is strings in the form returned by poppler_named_dest_to_bytestring() which constains a destination name. The tree value is the PopplerDest which contains a named destination. -The return value must be freed with g_tree_destroy().

+The return value must be freed with g_tree_destroy().

Parameters

 

gchar *named_dest;

gchar *named_dest;

name of the destination (POPPLER_DEST_NAMED only)

 

guint change_left : 1;

guint change_left : 1;

whether left coordinate should be changed

 

guint change_top : 1;

guint change_top : 1;

whether top coordinate should be changed

 

guint change_zoom : 1;

guint change_zoom : 1;

whether scale factor should be changed

 
 

gchar *title;

gchar *title;

action title

 
 

gchar *title;

gchar *title;

action title

 
 

gchar *title;

gchar *title;

action title

 

gchar *file_name;

gchar *file_name;

file name

 
 

gchar *title;

gchar *title;

action title

 

gchar *file_name;

gchar *file_name;

file name

 

gchar *params;

gchar *params;

parameters

 
 

gchar *title;

gchar *title;

action title

 
 

gchar *title;

gchar *title;

action title

 

gchar *named_dest;

gchar *named_dest;

named destination

 
 

gchar *title;

gchar *title;

action title

 
 

gchar *title;

gchar *title;

action title

 

gint op;

gint op;

operation

 
 

gchar *title;

gchar *title;

action title

 

GList *fields;

GList *fields;

list of field names to reset / retain.

[element-type utf8][nullable]

gboolean exclude;

gboolean exclude;

whether to reset all but the listed fields

 
 

gchar *title;

gchar *title;

action title

 

GList *state_list;

GList *state_list;

list of PopplerActionLayers.

[element-type PopplerActionLayer]
 

gchar *title;

gchar *title;

action title

 

gchar *script;

gchar *script;

javascript

 
 

GList *layers;

GList *layers;

list of PopplerLayers.

[element-type PopplerLayer]
-gboolean +gboolean (*PopplerAttachmentSaveFunc) ()
const GString * +const GString * poppler_attachment_get_checksum () @@ -57,14 +57,14 @@

Popp

-GDateTime * +GDateTime * poppler_attachment_get_ctime ()
const gchar * +const gchar * poppler_attachment_get_description () @@ -72,14 +72,14 @@

Popp

-GDateTime * +GDateTime * poppler_attachment_get_mtime ()
const gchar * +const gchar * poppler_attachment_get_name () @@ -87,7 +87,7 @@

Popp

-gsize +gsize poppler_attachment_get_size () @@ -95,7 +95,7 @@

Popp

-gboolean +gboolean poppler_attachment_save () @@ -103,7 +103,7 @@

Popp

-gboolean +gboolean poppler_attachment_save_to_fd () @@ -111,7 +111,7 @@

Popp

-gboolean +gboolean poppler_attachment_save_to_callback () @@ -135,7 +135,7 @@

Popp

Object Hierarchy

-
    GObject
+
    GObject
     ╰── PopplerAttachment
 
@@ -151,17 +151,17 @@

Popp

Functions

PopplerAttachmentSaveFunc ()

-
gboolean
-(*PopplerAttachmentSaveFunc) (const gchar *buf,
-                              gsize count,
-                              gpointer data,
-                              GError **error);
+
gboolean
+(*PopplerAttachmentSaveFunc) (const gchar *buf,
+                              gsize count,
+                              gpointer data,
+                              GError **error);

Specifies the type of the function passed to poppler_attachment_save_to_callback(). It is called once for each block of bytes that is "written" by poppler_attachment_save_to_callback(). If -successful it should return TRUE. If an error occurs it should set +successful it should return TRUE. If an error occurs it should set error - and return FALSE, in which case poppler_attachment_save_to_callback() + and return FALSE, in which case poppler_attachment_save_to_callback() will fail with the same error.

Parameters

@@ -191,7 +191,7 @@

Popp

error

GError to set on error, or NULL

GError to set on error, or NULL

 

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]

gchar *name;

gchar *name;

The filename. Deprecated in poppler 20.09.0. Use poppler_attachment_get_name() instead.

 

gchar *description;

gchar *description;

Descriptive text. Deprecated in poppler 20.09.0. Use poppler_attachment_get_description() instead.

 

gsize size;

gsize size;

The size of the file. Deprecated in poppler 20.09.0. Use poppler_attachment_get_size() instead.

 

GTime mtime;

GTime mtime;

The date and time when the file was last modified. Deprecated in poppler 20.09.0. Use poppler_attachment_get_mtime() instead.

 

GTime ctime;

GTime ctime;

The date and time when the file was created. Deprecated in poppler 20.09.0. Use poppler_attachment_get_ctime() instead.

 

GString *checksum;

GString *checksum;

A 16-byte checksum of the file. Deprecated in poppler 20.09.0. Use poppler_attachment_get_checksum() instead.

 
-GTree * +GTree * poppler_document_create_dests_tree () @@ -59,7 +59,7 @@

Popple

-GList * +GList * poppler_document_get_attachments () @@ -67,7 +67,7 @@

Popple

-gchar * +gchar * poppler_document_get_author () @@ -83,7 +83,7 @@

Popple

-GDateTime * +GDateTime * poppler_document_get_creation_date_time () @@ -91,7 +91,7 @@

Popple

-gchar * +gchar * poppler_document_get_creator () @@ -107,7 +107,7 @@

Popple

-gboolean +gboolean poppler_document_get_id () @@ -115,7 +115,7 @@

Popple

-gchar * +gchar * poppler_document_get_keywords () @@ -123,7 +123,7 @@

Popple

-gchar * +gchar * poppler_document_get_metadata () @@ -139,7 +139,7 @@

Popple

-GDateTime * +GDateTime * poppler_document_get_modification_date_time () @@ -147,7 +147,7 @@

Popple

-guint +guint poppler_document_get_n_attachments () @@ -163,7 +163,7 @@

Popple

-gint +gint poppler_document_get_n_signatures () @@ -227,7 +227,7 @@

Popple

-gchar * +gchar * poppler_document_get_pdf_subtype_string () @@ -243,7 +243,7 @@

Popple

-gchar * +gchar * poppler_document_get_pdf_version_string () @@ -267,7 +267,7 @@

Popple

-gint +gint poppler_document_get_print_n_copies () @@ -291,7 +291,7 @@

Popple

-gchar * +gchar * poppler_document_get_producer () @@ -299,7 +299,7 @@

Popple

-GList * +GList * poppler_document_get_signature_fields () @@ -307,7 +307,7 @@

Popple

-gchar * +gchar * poppler_document_get_subject () @@ -315,7 +315,7 @@

Popple

-gchar * +gchar * poppler_document_get_title () @@ -323,7 +323,7 @@

Popple

-gboolean +gboolean poppler_document_has_attachments () @@ -331,7 +331,7 @@

Popple

-gboolean +gboolean poppler_document_has_javascript () @@ -339,7 +339,7 @@

Popple

-gboolean +gboolean poppler_document_is_linearized () @@ -403,7 +403,7 @@

Popple

-gboolean +gboolean poppler_document_save () @@ -411,7 +411,7 @@

Popple

-gboolean +gboolean poppler_document_save_a_copy () @@ -419,7 +419,7 @@

Popple

-gboolean +gboolean poppler_document_save_to_fd () @@ -523,7 +523,7 @@

Popple

-gboolean +gboolean poppler_font_info_scan () @@ -590,7 +590,7 @@

Popple

-gboolean +gboolean poppler_fonts_iter_is_embedded () @@ -598,7 +598,7 @@

Popple

-gboolean +gboolean poppler_fonts_iter_is_subset () @@ -606,7 +606,7 @@

Popple

-gboolean +gboolean poppler_fonts_iter_next () @@ -646,7 +646,7 @@

Popple

-gboolean +gboolean poppler_index_iter_is_open () @@ -662,7 +662,7 @@

Popple

-gboolean +gboolean poppler_index_iter_next () @@ -702,7 +702,7 @@

Popple

-gchar * +gchar * poppler_layers_iter_get_title () @@ -718,7 +718,7 @@

Popple

-gboolean +gboolean poppler_layers_iter_next () @@ -789,7 +789,7 @@

Popple

-GDateTime * creation-datetime Read / Write
Read
guintguint format-major Read
guintguint format-minor Read
Read / Write
gbooleangboolean linearized Read
-GDateTime * mod-datetime Read / Write
@@ -1061,7 +1061,7 @@

Popple

Returns

-

the GTree, or NULL.

+

the GTree, or NULL.

[transfer full][nullable]

Since: 0.78

@@ -1071,7 +1071,7 @@

Popple

poppler_document_find_dest ()

PopplerDest *
 poppler_document_find_dest (PopplerDocument *document,
-                            const gchar *link_name);
+ const gchar *link_name);

Creates a PopplerDest for the named destination link_name in document .

@@ -1106,7 +1106,7 @@

Popple

Returns

-

a new PopplerDest destination, or NULL if +

a new PopplerDest destination, or NULL if link_name is not a destination.

[transfer full]

@@ -1115,11 +1115,11 @@

Popple

poppler_document_get_attachments ()

-
GList *
+
GList *
 poppler_document_get_attachments (PopplerDocument *document);
-

Returns a GList containing PopplerAttachments. These attachments +

Returns a GList containing PopplerAttachments. These attachments are unowned, and must be unreffed, and the list must be freed with -g_list_free().

+g_list_free().

Parameters

@@ -1144,7 +1144,7 @@

Popple

poppler_document_get_author ()

-
gchar *
+
gchar *
 poppler_document_get_author (PopplerDocument *document);

Returns the author of the document

@@ -1166,7 +1166,7 @@

Popple

Returns

a new allocated string containing the author of document -, or NULL

+, or NULL

Since: 0.16

@@ -1200,10 +1200,10 @@

Popple

poppler_document_get_creation_date_time ()

-
GDateTime *
+
GDateTime *
 poppler_document_get_creation_date_time
                                (PopplerDocument *document);
-

Returns the date the document was created as a GDateTime

+

Returns the date the document was created as a GDateTime

Parameters

@@ -1221,7 +1221,7 @@

Popple

Returns

-

the date the document was created, or NULL.

+

the date the document was created, or NULL.

[nullable]

Since: 20.09.0

@@ -1229,7 +1229,7 @@

Popple

poppler_document_get_creator ()

-
gchar *
+
gchar *
 poppler_document_get_creator (PopplerDocument *document);

Returns the creator of the document. If the document was converted from another format, the creator is the name of the product @@ -1253,7 +1253,7 @@

Popple

Returns

a new allocated string containing the creator of document -, or NULL

+, or NULL

Since: 0.16

@@ -1262,10 +1262,10 @@

Popple

poppler_document_get_form_field ()

PopplerFormField *
 poppler_document_get_form_field (PopplerDocument *document,
-                                 gint id);
+ gint id);

Returns the PopplerFormField for the given id . It must be freed with -g_object_unref()

+g_object_unref()

Parameters

@@ -1290,7 +1290,7 @@

Popple

Returns

-

a new PopplerFormField or NULL if +

a new PopplerFormField or NULL if not found.

[transfer full]

@@ -1298,10 +1298,10 @@

Popple

poppler_document_get_id ()

-
gboolean
+
gboolean
 poppler_document_get_id (PopplerDocument *document,
-                         gchar **permanent_id,
-                         gchar **update_id);
+ gchar **permanent_id, + gchar **update_id);

Returns the PDF file identifier represented as two byte string arrays of size 32. permanent_id is the permanent identifier that is built based on the file @@ -1327,12 +1327,12 @@

Popple

- + - + @@ -1340,15 +1340,15 @@

Popple

Returns

-

TRUE if the document -contains an id, FALSE otherwise

+

TRUE if the document +contains an id, FALSE otherwise

Since: 0.16


poppler_document_get_keywords ()

-
gchar *
+
gchar *
 poppler_document_get_keywords (PopplerDocument *document);

Returns the keywords associated to the document

@@ -1370,14 +1370,14 @@

Popple

Returns

a new allocated string containing keywords associated to document -, or NULL

+, or NULL

Since: 0.16


poppler_document_get_metadata ()

-
gchar *
+
gchar *
 poppler_document_get_metadata (PopplerDocument *document);

Returns the XML metadata string of the document

@@ -1398,7 +1398,7 @@

Popple

Returns

a new allocated string containing the XML -metadata, or NULL

+metadata, or NULL

Since: 0.16

@@ -1433,10 +1433,10 @@

Popple

poppler_document_get_modification_date_time ()

-
GDateTime *
+
GDateTime *
 poppler_document_get_modification_date_time
                                (PopplerDocument *document);
-

Returns the date the document was most recently modified as a GDateTime

+

Returns the date the document was most recently modified as a GDateTime

Parameters

permanent_id

location to store an allocated string, use g_free() to free the returned string.

location to store an allocated string, use g_free() to free the returned string.

[out][allow-none]

update_id

location to store an allocated string, use g_free() to free the returned string.

location to store an allocated string, use g_free() to free the returned string.

[out][allow-none]
@@ -1454,7 +1454,7 @@

Popple

Returns

-

the date the document was modified, or NULL.

+

the date the document was modified, or NULL.

[nullable]

Since: 20.09.0

@@ -1462,7 +1462,7 @@

Popple

poppler_document_get_n_attachments ()

-
guint
+
guint
 poppler_document_get_n_attachments (PopplerDocument *document);

Returns the number of attachments in a loaded document. See also poppler_document_get_attachments()

@@ -1516,7 +1516,7 @@

Popple

poppler_document_get_n_signatures ()

-
gint
+
gint
 poppler_document_get_n_signatures (const PopplerDocument *document);

Returns how many digital signatures document contains. @@ -1763,7 +1763,7 @@

Popple

poppler_document_get_pdf_subtype_string ()

-
gchar *
+
gchar *
 poppler_document_get_pdf_subtype_string
                                (PopplerDocument *document);

Returns the PDF subtype version of document @@ -1787,7 +1787,7 @@

Popple

Returns

a newly allocated string containing the PDF subtype version of document -, or NULL.

+, or NULL.

[transfer full][nullable]

Since: 0.70

@@ -1797,8 +1797,8 @@

Popple

poppler_document_get_pdf_version ()

void
 poppler_document_get_pdf_version (PopplerDocument *document,
-                                  guint *major_version,
-                                  guint *minor_version);
+ guint *major_version, + guint *minor_version);

Updates values referenced by major_version & minor_version with the @@ -1836,7 +1836,7 @@

Popple

poppler_document_get_pdf_version_string ()

-
gchar *
+
gchar *
 poppler_document_get_pdf_version_string
                                (PopplerDocument *document);

Returns the PDF version of document @@ -1860,7 +1860,7 @@

Popple

Returns

a new allocated string containing the PDF version of document -, or NULL

+, or NULL

Since: 0.16

@@ -1923,7 +1923,7 @@

Popple

poppler_document_get_print_n_copies ()

-
gint
+
gint
 poppler_document_get_print_n_copies (PopplerDocument *document);

Returns the suggested number of copies to be printed. This preference should be applied only if returned value @@ -1959,7 +1959,7 @@

Popple int *n_ranges);

Returns the suggested page ranges to print in the form of array of PopplerPageRanges and number of ranges. -NULL pointer means that the document does not specify page ranges +NULL pointer means that the document does not specify page ranges for printing.

Parameters

@@ -1986,7 +1986,7 @@

Popple

Returns

an array -of PopplerPageRanges or NULL. Free the array when +of PopplerPageRanges or NULL. Free the array when it is no longer needed.

[array length=n_ranges][transfer full]

@@ -2022,7 +2022,7 @@

Popple

poppler_document_get_producer ()

-
gchar *
+
gchar *
 poppler_document_get_producer (PopplerDocument *document);

Returns the producer of the document. If the document was converted from another format, the producer is the name of the product @@ -2046,16 +2046,16 @@

Popple

Returns

a new allocated string containing the producer of document -, or NULL

+, or NULL

Since: 0.16


poppler_document_get_signature_fields ()

-
GList *
+
GList *
 poppler_document_get_signature_fields (PopplerDocument *document);
-

Returns a GList containing all signature PopplerFormFields in the document.

+

Returns a GList containing all signature PopplerFormFields in the document.

Parameters

@@ -2081,7 +2081,7 @@

Popple

poppler_document_get_subject ()

-
gchar *
+
gchar *
 poppler_document_get_subject (PopplerDocument *document);

Returns the subject of the document

@@ -2103,14 +2103,14 @@

Popple

Returns

a new allocated string containing the subject of document -, or NULL

+, or NULL

Since: 0.16


poppler_document_get_title ()

-
gchar *
+
gchar *
 poppler_document_get_title (PopplerDocument *document);

Returns the document's title

@@ -2132,16 +2132,16 @@

Popple

Returns

a new allocated string containing the title of document -, or NULL

+, or NULL

Since: 0.16


poppler_document_has_attachments ()

-
gboolean
+
gboolean
 poppler_document_has_attachments (PopplerDocument *document);
-

Returns TRUE of document +

Returns TRUE of document has any attachments.

Parameters

@@ -2160,14 +2160,14 @@

Popple

Returns

-

TRUE, if document +

TRUE, if document has attachments.


poppler_document_has_javascript ()

-
gboolean
+
gboolean
 poppler_document_has_javascript (PopplerDocument *document);

Returns whether document has any javascript in it.

@@ -2191,7 +2191,7 @@

Popple

poppler_document_is_linearized ()

-
gboolean
+
gboolean
 poppler_document_is_linearized (PopplerDocument *document);

Returns whether document is linearized or not. Linearization of PDF @@ -2213,8 +2213,8 @@

Popple

Returns

-

TRUE if document -is linearized, FALSE otherwise

+

TRUE if document +is linearized, FALSE otherwise

Since: 0.16

@@ -2222,16 +2222,16 @@

Popple

poppler_document_new_from_bytes ()

PopplerDocument *
-poppler_document_new_from_bytes (GBytes *bytes,
+poppler_document_new_from_bytes (GBytes *bytes,
                                  const char *password,
-                                 GError **error);
+ GError **error);

Creates a new PopplerDocument from bytes . The returned document will hold a reference to bytes .

-

On error, NULL is returned, with error +

On error, NULL is returned, with error set. Possible errors include -those in the POPPLER_ERROR and G_FILE_ERROR domains.

+those in the POPPLER_ERROR and G_FILE_ERROR domains.

Parameters

@@ -2243,17 +2243,17 @@

Popple

- + - + - + @@ -2261,7 +2261,7 @@

Popple

Returns

-

a newly created PopplerDocument, or NULL.

+

a newly created PopplerDocument, or NULL.

[transfer full]

Since: 0.82

@@ -2273,7 +2273,7 @@

Popple poppler_document_new_from_data (char *data, int length, const char *password, - GError **error); + GError **error);

poppler_document_new_from_data has been deprecated since version 0.82 and should not be used in newly-written code.

This requires directly managing length @@ -2281,9 +2281,9 @@

Popple . Use poppler_document_new_from_bytes() instead.

-

Creates a new PopplerDocument. If NULL is returned, then error +

Creates a new PopplerDocument. If NULL is returned, then error will be -set. Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR +set. Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR domains.

Note that data is not copied nor is a new reference to it created. @@ -2310,12 +2310,12 @@

Popple

- + - + @@ -2323,7 +2323,7 @@

Popple

Returns

-

A newly created PopplerDocument, or NULL

+

A newly created PopplerDocument, or NULL


@@ -2332,13 +2332,13 @@

Popple
PopplerDocument *
 poppler_document_new_from_fd (int fd,
                               const char *password,
-                              GError **error);
+ GError **error);

Creates a new PopplerDocument reading the PDF contents from the file descriptor fd . fd must refer to a regular file, or STDIN, and be open for reading. -Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR +Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR domains. Note that this function takes ownership of fd ; you must not operate on it @@ -2359,12 +2359,12 @@

Popple

- + - + @@ -2372,7 +2372,7 @@

Popple

Returns

-

a new PopplerDocument, or NULL.

+

a new PopplerDocument, or NULL.

[transfer full]

Since: 21.12.0

@@ -2383,10 +2383,10 @@

Popple
PopplerDocument *
 poppler_document_new_from_file (const char *uri,
                                 const char *password,
-                                GError **error);
-

Creates a new PopplerDocument. If NULL is returned, then error + GError **error); +

Creates a new PopplerDocument. If NULL is returned, then error will be -set. Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR +set. Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR domains.

Parameters

@@ -2404,12 +2404,12 @@

Popple

- + - + @@ -2417,20 +2417,20 @@

Popple

Returns

-

A newly created PopplerDocument, or NULL

+

A newly created PopplerDocument, or NULL


poppler_document_new_from_gfile ()

PopplerDocument *
-poppler_document_new_from_gfile (GFile *file,
+poppler_document_new_from_gfile (GFile *file,
                                  const char *password,
-                                 GCancellable *cancellable,
-                                 GError **error);
+ GCancellable *cancellable, + GError **error);

Creates a new PopplerDocument reading the PDF contents from file . -Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR +Possible errors include those in the POPPLER_ERROR and G_FILE_ERROR domains.

Parameters

@@ -2443,22 +2443,22 @@

Popple

- + - + - + - + @@ -2466,7 +2466,7 @@

Popple

Returns

-

a new PopplerDocument, or NULL.

+

a new PopplerDocument, or NULL.

[transfer full]

Since: 0.22

@@ -2475,17 +2475,17 @@

Popple

poppler_document_new_from_stream ()

PopplerDocument *
-poppler_document_new_from_stream (GInputStream *stream,
-                                  goffset length,
+poppler_document_new_from_stream (GInputStream *stream,
+                                  goffset length,
                                   const char *password,
-                                  GCancellable *cancellable,
-                                  GError **error);
+ GCancellable *cancellable, + GError **error);

Creates a new PopplerDocument reading the PDF contents from stream . -Note that the given GInputStream must be seekable or G_IO_ERROR_NOT_SUPPORTED +Note that the given GInputStream must be seekable or G_IO_ERROR_NOT_SUPPORTED will be returned. -Possible errors include those in the POPPLER_ERROR, G_FILE_ERROR -and G_IO_ERROR domains.

+Possible errors include those in the POPPLER_ERROR, G_FILE_ERROR +and G_IO_ERROR domains.

Parameters

bytes

a GBytes

a GBytes

 

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[allow-none]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[allow-none]

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[nullable]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[nullable]

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[allow-none]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[allow-none]

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[allow-none]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[allow-none]

file

a GFile to load

a GFile to load

 

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[allow-none]

cancellable

a GCancellable, or NULL.

a GCancellable, or NULL.

[allow-none]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[allow-none]
@@ -2497,7 +2497,7 @@

Popple

- + @@ -2507,17 +2507,17 @@

Popple

- + - + - + @@ -2525,7 +2525,7 @@

Popple

Returns

-

a new PopplerDocument, or NULL.

+

a new PopplerDocument, or NULL.

[transfer full]

Since: 0.22

@@ -2535,8 +2535,8 @@

Popple

poppler_document_reset_form ()

void
 poppler_document_reset_form (PopplerDocument *document,
-                             GList *fields,
-                             gboolean exclude_fields);
+ GList *fields, + gboolean exclude_fields);

Resets the form fields specified by fields if exclude_fields is FALSE. Resets all others if exclude_fields is TRUE. All form fields are reset regardless of the exclude_fields flag @@ -2574,17 +2574,17 @@

Popple

poppler_document_save ()

-
gboolean
+
gboolean
 poppler_document_save (PopplerDocument *document,
                        const char *uri,
-                       GError **error);
+ GError **error);

Saves document . Any change made in the document such as form fields filled, annotations added or modified will be saved. If error - is set, FALSE will be returned. Possible errors -include those in the G_FILE_ERROR domain.

+ is set, FALSE will be returned. Possible errors +include those in the G_FILE_ERROR domain.

Parameters

stream

a GInputStream to read from

a GInputStream to read from

 

password

password to unlock the file with, or NULL.

password to unlock the file with, or NULL.

[allow-none]

cancellable

a GCancellable, or NULL.

a GCancellable, or NULL.

[allow-none]

error

Return location for an error, or NULL.

Return location for an error, or NULL.

[allow-none]
@@ -2606,7 +2606,7 @@

Popple

- + @@ -2614,23 +2614,23 @@

Popple

Returns

-

TRUE, if the document was successfully saved

+

TRUE, if the document was successfully saved


poppler_document_save_a_copy ()

-
gboolean
+
gboolean
 poppler_document_save_a_copy (PopplerDocument *document,
                               const char *uri,
-                              GError **error);
+ GError **error);

Saves a copy of the original document . Any change made in the document such as form fields filled by the user will not be saved. If error - is set, FALSE will be returned. Possible errors -include those in the G_FILE_ERROR domain.

+ is set, FALSE will be returned. Possible errors +include those in the G_FILE_ERROR domain.

Parameters

error

return location for an error, or NULL.

return location for an error, or NULL.

[allow-none]
@@ -2652,7 +2652,7 @@

Popple

- + @@ -2660,30 +2660,30 @@

Popple

Returns

-

TRUE, if the document was successfully saved

+

TRUE, if the document was successfully saved


poppler_document_save_to_fd ()

-
gboolean
+
gboolean
 poppler_document_save_to_fd (PopplerDocument *document,
                              int fd,
-                             gboolean include_changes,
-                             GError **error);
+ gboolean include_changes, + GError **error);

Saves document . Any change made in the document such as form fields filled, annotations added or modified will be saved if include_changes - is TRUE, or discarded i + is TRUE, or discarded i include_changes - is FALSE.

+ is FALSE.

Note that this function takes ownership of fd ; you must not operate on it again, nor close it.

If error - is set, FALSE will be returned. Possible errors -include those in the G_FILE_ERROR domain.

+ is set, FALSE will be returned. Possible errors +include those in the G_FILE_ERROR domain.

Parameters

error

return location for an error, or NULL.

return location for an error, or NULL.

[allow-none]
@@ -2710,7 +2710,7 @@

Popple

- + @@ -2718,7 +2718,7 @@

Popple

Returns

-

TRUE, if the document was successfully saved

+

TRUE, if the document was successfully saved

Since: 21.12.0

@@ -2727,9 +2727,9 @@

Popple

poppler_document_set_author ()

void
 poppler_document_set_author (PopplerDocument *document,
-                             const gchar *author);
+ const gchar *author);

Sets the document's author. If author - is NULL, Author + is NULL, Author entry is removed from the document's Info dictionary.

Parameters

@@ -2794,9 +2794,9 @@

Popple
void
 poppler_document_set_creation_date_time
                                (PopplerDocument *document,
-                                GDateTime *creation_datetime);
+ GDateTime *creation_datetime);

Sets the document's creation date. If creation_datetime - is NULL, + is NULL, CreationDate entry is removed from the document's Info dictionary.

Parameters

@@ -2814,7 +2814,7 @@

Popple

- + @@ -2827,9 +2827,9 @@

Popple

poppler_document_set_creator ()

void
 poppler_document_set_creator (PopplerDocument *document,
-                              const gchar *creator);
+ const gchar *creator);

Sets the document's creator. If creator - is NULL, Creator + is NULL, Creator entry is removed from the document's Info dictionary.

Parameters

@@ -2860,9 +2860,9 @@

Popple

poppler_document_set_keywords ()

void
 poppler_document_set_keywords (PopplerDocument *document,
-                               const gchar *keywords);
+ const gchar *keywords);

Sets the document's keywords. If keywords - is NULL, + is NULL, Keywords entry is removed from the document's Info dictionary.

Parameters

@@ -2928,9 +2928,9 @@

Popple
void
 poppler_document_set_modification_date_time
                                (PopplerDocument *document,
-                                GDateTime *modification_datetime);
+ GDateTime *modification_datetime);

Sets the document's modification date. If modification_datetime - is NULL, + is NULL, ModDate entry is removed from the document's Info dictionary.

Parameters

@@ -2948,7 +2948,7 @@

Popple

- + @@ -2961,9 +2961,9 @@

Popple

poppler_document_set_producer ()

void
 poppler_document_set_producer (PopplerDocument *document,
-                               const gchar *producer);
+ const gchar *producer);

Sets the document's producer. If producer - is NULL, + is NULL, Producer entry is removed from the document's Info dictionary.

Parameters

@@ -2994,9 +2994,9 @@

Popple

poppler_document_set_subject ()

void
 poppler_document_set_subject (PopplerDocument *document,
-                              const gchar *subject);
+ const gchar *subject);

Sets the document's subject. If subject - is NULL, Subject + is NULL, Subject entry is removed from the document's Info dictionary.

Parameters

@@ -3027,9 +3027,9 @@

Popple

poppler_document_set_title ()

void
 poppler_document_set_title (PopplerDocument *document,
-                            const gchar *title);
+ const gchar *title);

Sets the document's title. If title - is NULL, Title entry + is NULL, Title entry is removed from the document's Info dictionary.

Parameters

@@ -3090,7 +3090,7 @@

Popple

poppler_font_info_scan ()

-
gboolean
+
gboolean
 poppler_font_info_scan (PopplerFontInfo *font_info,
                         int n_pages,
                         PopplerFontsIter **iter);
@@ -3114,17 +3114,23 @@

Popple 7 8 9 -10

-

+10 +11 +12 +13 +

error

return location for an error, or NULL.

return location for an error, or NULL.

[allow-none]

creation_datetime

A new creation GDateTime.

A new creation GDateTime.

[nullable]

modification_datetime

A new modification GDateTime.

A new modification GDateTime.

[nullable]
font_info = poppler_font_info_new (document);
-while (poppler_font_info_scan (font_info, 20, &fonts_iter)) {
-        if (!fonts_iter)
-                continue; /* No fonts found in these 20 pages */
-        do {
-                /* Do something with font iter */
-                g_print ("Font Name: %s\n", poppler_fonts_iter_get_name (fonts_iter));
-        } while (poppler_fonts_iter_next (fonts_iter));
-        poppler_fonts_iter_free (fonts_iter);
-}
font_info = poppler_font_info_new (document);
+scanned_pages = 0;
+while (scanned_pages <= poppler_document_get_n_pages(document)) {
+        poppler_font_info_scan (font_info, 20, &fonts_iter);
+        scanned_pages += 20;
+        if (!fonts_iter)
+                continue; /* No fonts found in these 20 pages */
+        do {
+                /* Do something with font iter */
+                g_print ("Font Name: %s\n", poppler_fonts_iter_get_name (fonts_iter));
+        } while (poppler_fonts_iter_next (fonts_iter));
+        poppler_fonts_iter_free (fonts_iter);
+}
@@ -3159,7 +3165,7 @@

Popple

Returns

-

TRUE, if there are more fonts left to scan

+

TRUE, if fonts were found


@@ -3246,7 +3252,7 @@

Popple
const char *
 poppler_fonts_iter_get_file_name (PopplerFontsIter *iter);

The filename of the font associated with iter - or NULL if + or NULL if the font is embedded

Parameters

@@ -3265,7 +3271,7 @@

Popple

Returns

-

the filename of the font or NULL if font is embedded

+

the filename of the font or NULL if font is embedded


@@ -3356,7 +3362,7 @@

Popple poppler_fonts_iter_get_substitute_name (PopplerFontsIter *iter);

The name of the substitute font of the font associated with iter - or NULL if + or NULL if the font is embedded

Parameters

@@ -3375,14 +3381,14 @@

Popple

Returns

-

the name of the substitute font or NULL if font is embedded

+

the name of the substitute font or NULL if font is embedded

Since: 0.20


poppler_fonts_iter_is_embedded ()

-
gboolean
+
gboolean
 poppler_fonts_iter_is_embedded (PopplerFontsIter *iter);

Returns whether the font associated with iter is embedded in the document

@@ -3403,13 +3409,13 @@

Popple

Returns

-

TRUE if font is embedded, FALSE otherwise

+

TRUE if font is embedded, FALSE otherwise


poppler_fonts_iter_is_subset ()

-
gboolean
+
gboolean
 poppler_fonts_iter_is_subset (PopplerFontsIter *iter);

Returns whether the font associated with iter is a subset of another font

@@ -3430,13 +3436,13 @@

Popple

Returns

-

TRUE if font is a subset, FALSE otherwise

+

TRUE if font is a subset, FALSE otherwise


poppler_fonts_iter_next ()

-
gboolean
+
gboolean
 poppler_fonts_iter_next (PopplerFontsIter *iter);

Sets iter to point to the next font

@@ -3457,7 +3463,7 @@

Popple

Returns

-

TRUE, if iter +

TRUE, if iter was set to the next font

@@ -3546,7 +3552,7 @@

Popple
PopplerIndexIter *
 poppler_index_iter_get_child (PopplerIndexIter *parent);

Returns a newly created child of parent -, or NULL if the iter has no child. +, or NULL if the iter has no child. See poppler_index_iter_new() for more information on this function.

Parameters

@@ -3571,7 +3577,7 @@

Popple

poppler_index_iter_is_open ()

-
gboolean
+
gboolean
 poppler_index_iter_is_open (PopplerIndexIter *iter);

Returns whether this node should be expanded by default to the user. The document can provide a hint as to how the document's index should be expanded @@ -3593,7 +3599,7 @@

Popple

Returns

-

TRUE, if the document wants iter +

TRUE, if the document wants iter to be expanded

@@ -3603,7 +3609,7 @@

Popple
PopplerIndexIter *
 poppler_index_iter_new (PopplerDocument *document);

Returns the root PopplerIndexIter for document -, or NULL. This must be +, or NULL. This must be freed with poppler_index_iter_free().

Certain documents have an index associated with them. This index can be used to help the user navigate the document, and is similar to a table of @@ -3634,25 +3640,25 @@

Popple 17 18 19 -
static void
-walk_index (PopplerIndexIter *iter)
-{
-  do
-    {
-      /* Get the action and do something with it */
-      PopplerIndexIter *child = poppler_index_iter_get_child (iter);
-      if (child)
-        walk_index (child);
-      poppler_index_iter_free (child);
-    }
-  while (poppler_index_iter_next (iter));
-}
-...
-{
-  iter = poppler_index_iter_new (document);
-  walk_index (iter);
-  poppler_index_iter_free (iter);
-}
+
static void
+walk_index (PopplerIndexIter *iter)
+{
+  do
+    {
+      /* Get the action and do something with it */
+      PopplerIndexIter *child = poppler_index_iter_get_child (iter);
+      if (child)
+        walk_index (child);
+      poppler_index_iter_free (child);
+    }
+  while (poppler_index_iter_next (iter));
+}
+...
+{
+  iter = poppler_index_iter_new (document);
+  walk_index (iter);
+  poppler_index_iter_free (iter);
+}
@@ -3681,7 +3687,7 @@

Popple

poppler_index_iter_next ()

-
gboolean
+
gboolean
 poppler_index_iter_next (PopplerIndexIter *iter);

Sets iter to point to the next action at the current level, if valid. See @@ -3703,7 +3709,7 @@

Popple

Returns

-

TRUE, if iter +

TRUE, if iter was set to the next action

@@ -3766,7 +3772,7 @@

Popple
PopplerLayersIter *
 poppler_layers_iter_get_child (PopplerLayersIter *parent);

Returns a newly created child of parent -, or NULL if the iter has no child. +, or NULL if the iter has no child. See poppler_layers_iter_new() for more information on this function.

Parameters

@@ -3785,7 +3791,7 @@

Popple

Returns

-

a new PopplerLayersIter, or NULL

+

a new PopplerLayersIter, or NULL

Since: 0.12

@@ -3813,7 +3819,7 @@

Popple

Returns

-

a new PopplerLayer, or NULL if +

a new PopplerLayer, or NULL if there isn't any layer associated with iter .

[transfer full]

@@ -3823,11 +3829,11 @@

Popple

poppler_layers_iter_get_title ()

-
gchar *
+
gchar *
 poppler_layers_iter_get_title (PopplerLayersIter *iter);

Returns the title associated with iter . It must be freed with -g_free().

+g_free().

Parameters

@@ -3846,9 +3852,9 @@

Popple

Returns

a new string containing the iter -'s title or NULL if iter +'s title or NULL if iter doesn't have a title. -The returned string should be freed with g_free() when no longer needed.

+The returned string should be freed with g_free() when no longer needed.

Since: 0.12

@@ -3877,7 +3883,7 @@

Popple

poppler_layers_iter_next ()

-
gboolean
+
gboolean
 poppler_layers_iter_next (PopplerLayersIter *iter);

Sets iter to point to the next action at the current level, if valid. See @@ -3899,7 +3905,7 @@

Popple

Returns

-

TRUE, if iter +

TRUE, if iter was set to the next action

Since: 0.12

@@ -4030,7 +4036,7 @@

Popple

poppler_ps_file_set_duplex ()

void
 poppler_ps_file_set_duplex (PopplerPSFile *ps_file,
-                            gboolean duplex);
+ gboolean duplex);

Enable or disable Duplex printing.

Parameters

@@ -4104,6 +4110,8 @@

Popple

PopplerFontInfo

typedef struct _PopplerFontInfo PopplerFontInfo;
+

Interface for getting the Fonts of a poppler_document

+

Since 24.10 this type supports g_autoptr


@@ -4210,16 +4218,21 @@

Popple

PopplerFontsIter

typedef struct _PopplerFontsIter PopplerFontsIter;
+

Since 24.10 this type supports g_autoptr


PopplerIndexIter

typedef struct _PopplerIndexIter PopplerIndexIter;
+

Interface for getting the Index of a poppler_document

+

Since 24.10 this type supports g_autoptr


PopplerLayersIter

typedef struct _PopplerLayersIter PopplerLayersIter;
+

Interface for getting the Layers of a poppler_document

+

Since 24.10 this type supports g_autoptr


@@ -4613,12 +4626,12 @@

Popple

- + - + @@ -4889,7 +4902,7 @@

Popple

The “creation-datetime” property

-
  “creation-datetime”        GDateTime *
+
  “creation-datetime”        GDateTime *

The date and time the document was created.

Owner: PopplerDocument

Flags: Read / Write

@@ -4916,7 +4929,7 @@

Popple

The “format-major” property

-
  “format-major”             guint
+
  “format-major”             guint

The PDF major version number. See also poppler_document_get_pdf_version()

Owner: PopplerDocument

Flags: Read

@@ -4925,7 +4938,7 @@

Popple

The “format-minor” property

-
  “format-minor”             guint
+
  “format-minor”             guint

The PDF minor version number. See also poppler_document_get_pdf_version()

Owner: PopplerDocument

Flags: Read

@@ -4943,7 +4956,7 @@

Popple

The “linearized” property

-
  “linearized”               gboolean
+
  “linearized”               gboolean

Whether document is linearized. See also poppler_document_is_linearized()

Owner: PopplerDocument

Flags: Read

@@ -4953,7 +4966,7 @@

Popple

The “metadata” property

  “metadata”                 char *
-

Document metadata in XML format, or NULL

+

Document metadata in XML format, or NULL

Owner: PopplerDocument

Flags: Read

Default value: NULL

@@ -4975,8 +4988,8 @@

Popple

The “mod-datetime” property

-
  “mod-datetime”             GDateTime *
-

The GDateTime the document was most recently modified.

+
  “mod-datetime”             GDateTime *
+

The GDateTime the document was most recently modified.

Owner: PopplerDocument

Flags: Read / Write

Since: 20.09.0

@@ -5097,7 +5110,7 @@

Popple

The “title” property

  “title”                    char *
-

The document's title or NULL

+

The document's title or NULL

Owner: PopplerDocument

Flags: Read / Write

Default value: NULL

@@ -5113,6 +5126,6 @@

Popple

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/PopplerFormField.html b/glib/reference/html/PopplerFormField.html index c8e762e..7273c8b 100644 --- a/glib/reference/html/PopplerFormField.html +++ b/glib/reference/html/PopplerFormField.html @@ -8,7 +8,7 @@ - + @@ -42,6 +42,64 @@

Poppl

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - -

gint start_page;

gint start_page;

first page in the range of pages

 

gint end_page;

gint end_page;

last page in the range of pages

 
+GDateTime * + +poppler_certificate_info_get_expiration_time () +
+GDateTime * + +poppler_certificate_info_get_issuance_time () +
const char * + +poppler_certificate_info_get_issuer_common_name () +
const char * + +poppler_certificate_info_get_issuer_email () +
const char * + +poppler_certificate_info_get_issuer_organization () +
const char * + +poppler_certificate_info_get_subject_common_name () +
const char * + +poppler_certificate_info_get_subject_email () +
const char * + +poppler_certificate_info_get_subject_organization () +
PopplerFormButtonType @@ -50,7 +108,7 @@

Poppl

-gboolean +gboolean poppler_form_field_button_get_state () @@ -66,7 +124,7 @@

Poppl

-gboolean +gboolean poppler_form_field_choice_can_select_multiple () @@ -74,7 +132,7 @@

Poppl

-gboolean +gboolean poppler_form_field_choice_commit_on_change () @@ -82,7 +140,7 @@

Poppl

-gboolean +gboolean poppler_form_field_choice_do_spell_check () @@ -98,7 +156,7 @@

Poppl

-gchar * +gchar * poppler_form_field_choice_get_item () @@ -106,7 +164,7 @@

Poppl

-gint +gint poppler_form_field_choice_get_n_items () @@ -114,7 +172,7 @@

Poppl

-gchar * +gchar * poppler_form_field_choice_get_text () @@ -122,7 +180,7 @@

Poppl

-gboolean +gboolean poppler_form_field_choice_is_editable () @@ -130,7 +188,7 @@

Poppl

-gboolean +gboolean poppler_form_field_choice_is_item_selected () @@ -186,7 +244,7 @@

Poppl

-gchar * +gchar * poppler_form_field_get_alternate_ui_name () @@ -202,7 +260,7 @@

Poppl

-gdouble +gdouble poppler_form_field_get_font_size () @@ -210,7 +268,7 @@

Poppl

-gint +gint poppler_form_field_get_id () @@ -218,7 +276,7 @@

Poppl

-gchar * +gchar * poppler_form_field_get_mapping_name () @@ -226,7 +284,7 @@

Poppl

-gchar * +gchar * poppler_form_field_get_name () @@ -234,7 +292,7 @@

Poppl

-gchar * +gchar * poppler_form_field_get_partial_name () @@ -242,7 +300,7 @@

Poppl

-gboolean +gboolean poppler_form_field_is_read_only () @@ -274,7 +332,7 @@

Poppl

-gboolean +gboolean poppler_form_field_text_do_scroll () @@ -282,7 +340,7 @@

Poppl

-gboolean +gboolean poppler_form_field_text_do_spell_check () @@ -290,7 +348,7 @@

Poppl

-gint +gint poppler_form_field_text_get_max_len () @@ -298,7 +356,7 @@

Poppl

-gchar * +gchar * poppler_form_field_text_get_text () @@ -314,7 +372,7 @@

Poppl

-gboolean +gboolean poppler_form_field_text_is_password () @@ -322,7 +380,7 @@

Poppl

-gboolean +gboolean poppler_form_field_text_is_rich_text () @@ -354,6 +412,14 @@

Poppl

+PopplerCertificateInfo * + +poppler_signature_info_get_certificate_info () +
PopplerCertificateStatus @@ -369,7 +435,7 @@

Poppl

const gchar * +const gchar * poppler_signature_info_get_signer_name () @@ -377,229 +443,2028 @@

Poppl

-GDateTime * +GDateTime * poppler_signature_info_get_local_signing_time ()
-
- -
-

Object Hierarchy

-
    GBoxed
-    ╰── PopplerSignatureInfo
-    GEnum
-    ├── PopplerAdditionalActionType
-    ├── PopplerCertificateStatus
-    ├── PopplerFormButtonType
-    ├── PopplerFormChoiceType
-    ├── PopplerFormFieldType
-    ├── PopplerFormTextType
-    ╰── PopplerSignatureStatus
-    GFlags
-    ╰── PopplerSignatureValidationFlags
-    GObject
-    ╰── PopplerFormField
-
-
-
-

Includes

-
#include <poppler.h>
-
-
-
-

Description

-
-
-

Functions

-
-

poppler_form_field_button_get_button_type ()

-
PopplerFormButtonType
-poppler_form_field_button_get_button_type
-                               (PopplerFormField *field);
-

Gets the button type of field -

-
-

Parameters

-
----- - - - - - -

field

a PopplerFormField

 
-
-
-

Returns

-

PopplerFormButtonType of field -

-
-
-
-
-

poppler_form_field_button_get_state ()

-
gboolean
-poppler_form_field_button_get_state (PopplerFormField *field);
-

Queries a PopplerFormField and returns its current state. Returns TRUE if -field - is pressed in and FALSE if it is raised.

-
-

Parameters

-
----- - - - - - -

field

a PopplerFormField

 
-
-
-

Returns

-

current state of field -

-
-
-
-
-

poppler_form_field_button_set_state ()

-
void
-poppler_form_field_button_set_state (PopplerFormField *field,
-                                     gboolean state);
-

Sets the status of field -. Set to TRUE if you want the PopplerFormField -to be 'pressed in', and FALSE to raise it.

-
-

Parameters

-
----- - - - - + + - - - + + - -

field

a PopplerFormField

  +void + +poppler_signing_data_set_font_size () +

state

TRUE or FALSE

  +void + +poppler_signing_data_set_left_font_size () +
-
-
-
-
-

poppler_form_field_choice_can_select_multiple ()

-
gboolean
-poppler_form_field_choice_can_select_multiple
-                               (PopplerFormField *field);
-

Checks whether field - allows multiple choices to be selected

-
-

Parameters

-
--+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+void + +poppler_signing_data_set_border_color () +
+void + +poppler_signing_data_set_border_width () +
+void + +poppler_signing_data_set_background_color () +
+void + +poppler_signing_data_set_field_partial_name () +
+void + +poppler_signing_data_set_reason () +
+void + +poppler_signing_data_set_location () +
+void + +poppler_signing_data_set_image_path () +
+void + +poppler_signing_data_set_password () +
+void + +poppler_signing_data_set_document_owner_password () +
+void + +poppler_signing_data_set_document_user_password () +
const gchar * + +poppler_signing_data_get_destination_filename () +
const PopplerCertificateInfo * + +poppler_signing_data_get_certificate_info () +
+int + +poppler_signing_data_get_page () +
const gchar * + +poppler_signing_data_get_signature_text () +
const gchar * + +poppler_signing_data_get_signature_text_left () +
const PopplerRectangle * + +poppler_signing_data_get_signature_rectangle () +
const PopplerColor * + +poppler_signing_data_get_font_color () +
+gdouble + +poppler_signing_data_get_font_size () +
+gdouble + +poppler_signing_data_get_left_font_size () +
const PopplerColor * + +poppler_signing_data_get_border_color () +
+gdouble + +poppler_signing_data_get_border_width () +
const PopplerColor * + +poppler_signing_data_get_background_color () +
const gchar * + +poppler_signing_data_get_field_partial_name () +
const gchar * + +poppler_signing_data_get_reason () +
const gchar * + +poppler_signing_data_get_location () +
const gchar * + +poppler_signing_data_get_image_path () +
const gchar * + +poppler_signing_data_get_password () +
const gchar * + +poppler_signing_data_get_document_owner_password () +
const gchar * + +poppler_signing_data_get_document_user_password () +
+PopplerCertificateInfo * + +poppler_certificate_info_new () +
+PopplerCertificateInfo * + +poppler_certificate_info_copy () +
+void + +poppler_set_nss_dir () +
+char * + +poppler_get_nss_dir () +
+void + +poppler_set_nss_password_callback () +
+GList * + +poppler_get_available_signing_certificates () +
+void + +poppler_certificate_info_free () +
+
+ +
+

Object Hierarchy

+
    GBoxed
+    ├── PopplerCertificateInfo
+    ╰── PopplerSignatureInfo
+    GEnum
+    ├── PopplerAdditionalActionType
+    ├── PopplerCertificateStatus
+    ├── PopplerFormButtonType
+    ├── PopplerFormChoiceType
+    ├── PopplerFormFieldType
+    ├── PopplerFormTextType
+    ╰── PopplerSignatureStatus
+    GFlags
+    ╰── PopplerSignatureValidationFlags
+    GObject
+    ╰── PopplerFormField
+
+
+
+

Includes

+
#include <poppler.h>
+
+
+
+

Description

+
+
+

Functions

+
+

poppler_certificate_info_get_expiration_time ()

+
GDateTime *
+poppler_certificate_info_get_expiration_time
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate expiration time

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate expiration time.

+

[transfer none]

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_issuance_time ()

+
GDateTime *
+poppler_certificate_info_get_issuance_time
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate issuance time

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate issuance time.

+

[transfer none]

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_issuer_common_name ()

+
const char *
+poppler_certificate_info_get_issuer_common_name
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate issuer common name

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate issuer common name

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_issuer_email ()

+
const char *
+poppler_certificate_info_get_issuer_email
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate issuer email

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate issuer email

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_issuer_organization ()

+
const char *
+poppler_certificate_info_get_issuer_organization
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate issuer organization

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate issuer organization

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_subject_common_name ()

+
const char *
+poppler_certificate_info_get_subject_common_name
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate subject common name

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate subject common name

+
+

Since: 23.07.0

+
+
+
+

poppler_certificate_info_get_subject_email ()

+
const char *
+poppler_certificate_info_get_subject_email
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate subject email

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate subject email

+
+

Since: 23.08.0

+
+
+
+

poppler_certificate_info_get_subject_organization ()

+
const char *
+poppler_certificate_info_get_subject_organization
+                               (const PopplerCertificateInfo *certificate_info);
+

Get certificate subject organization

+
+

Parameters

+
+++++ + + + + + +

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
+
+
+

Returns

+

certificate subject organization

+
+

Since: 23.08.0

+
+
+
+

poppler_form_field_button_get_button_type ()

+
PopplerFormButtonType
+poppler_form_field_button_get_button_type
+                               (PopplerFormField *field);
+

Gets the button type of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

PopplerFormButtonType of field +

+
+
+
+
+

poppler_form_field_button_get_state ()

+
gboolean
+poppler_form_field_button_get_state (PopplerFormField *field);
+

Queries a PopplerFormField and returns its current state. Returns TRUE if +field + is pressed in and FALSE if it is raised.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

current state of field +

+
+
+
+
+

poppler_form_field_button_set_state ()

+
void
+poppler_form_field_button_set_state (PopplerFormField *field,
+                                     gboolean state);
+

Sets the status of field +. Set to TRUE if you want the PopplerFormField +to be 'pressed in', and FALSE to raise it.

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

state

TRUE or FALSE

 
+
+
+
+
+

poppler_form_field_choice_can_select_multiple ()

+
gboolean
+poppler_form_field_choice_can_select_multiple
+                               (PopplerFormField *field);
+

Checks whether field + allows multiple choices to be selected

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if field +allows multiple choices to be selected

+
+
+
+
+

poppler_form_field_choice_commit_on_change ()

+
gboolean
+poppler_form_field_choice_commit_on_change
+                               (PopplerFormField *field);
+
+
+
+

poppler_form_field_choice_do_spell_check ()

+
gboolean
+poppler_form_field_choice_do_spell_check
+                               (PopplerFormField *field);
+

Checks whether spell checking should be done for the contents of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if spell checking should be done for field +

+
+
+
+
+

poppler_form_field_choice_get_choice_type ()

+
PopplerFormChoiceType
+poppler_form_field_choice_get_choice_type
+                               (PopplerFormField *field);
+

Gets the choice type of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

PopplerFormChoiceType of field +

+
+
+
+
+

poppler_form_field_choice_get_item ()

+
gchar *
+poppler_form_field_choice_get_item (PopplerFormField *field,
+                                    gint index);
+

Returns the contents of the item on field + at the given index

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

index

the index of the item

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+
+
+
+

poppler_form_field_choice_get_n_items ()

+
gint
+poppler_form_field_choice_get_n_items (PopplerFormField *field);
+

Returns the number of items on field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

the number of items on field +

+
+
+
+
+

poppler_form_field_choice_get_text ()

+
gchar *
+poppler_form_field_choice_get_text (PopplerFormField *field);
+

Retrieves the contents of field +.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+
+
+
+

poppler_form_field_choice_is_editable ()

+
gboolean
+poppler_form_field_choice_is_editable (PopplerFormField *field);
+

Checks whether field + is editable

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if field +is editable

+
+
+
+
+

poppler_form_field_choice_is_item_selected ()

+
gboolean
+poppler_form_field_choice_is_item_selected
+                               (PopplerFormField *field,
+                                gint index);
+

Checks whether the item at the given index on field + is currently selected

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

index

the index of the item

 
+
+
+

Returns

+

TRUE if item at index +is currently selected

+
+
+
+
+

poppler_form_field_choice_select_item ()

+
void
+poppler_form_field_choice_select_item (PopplerFormField *field,
+                                       gint index);
+

Selects the item at the given index on field +

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

index

the index of the item

 
+
+
+
+
+

poppler_form_field_choice_set_text ()

+
void
+poppler_form_field_choice_set_text (PopplerFormField *field,
+                                    const gchar *text);
+

Sets the text in field + to the given value, replacing the current contents

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

text

the new text

 
+
+
+
+
+

poppler_form_field_choice_toggle_item ()

+
void
+poppler_form_field_choice_toggle_item (PopplerFormField *field,
+                                       gint index);
+

Changes the state of the item at the given index

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

index

the index of the item

 
+
+
+
+
+

poppler_form_field_choice_unselect_all ()

+
void
+poppler_form_field_choice_unselect_all
+                               (PopplerFormField *field);
+

Unselects all the items on field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+
+
+

poppler_form_field_get_action ()

+
PopplerAction *
+poppler_form_field_get_action (PopplerFormField *field);
+

Retrieves the action (PopplerAction) that shall be +performed when field + is activated, or NULL

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

the action to perform. The returned +object is owned by field +and should not be freed.

+

[transfer none]

+
+

Since: 0.18

+
+
+
+

poppler_form_field_get_additional_action ()

+
PopplerAction *
+poppler_form_field_get_additional_action
+                               (PopplerFormField *field,
+                                PopplerAdditionalActionType type);
+

Retrieves the action (PopplerAction) that shall be performed when +an additional action is triggered on field +, or NULL.

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

type

the type of additional action

 
+
+
+

Returns

+

the action to perform. The returned +object is owned by field +and should not be freed.

+

[transfer none]

+
+

Since: 0.72

+
+
+
+

poppler_form_field_get_alternate_ui_name ()

+
gchar *
+poppler_form_field_get_alternate_ui_name
+                               (PopplerFormField *field);
+

Gets the alternate ui name of field +. This name is also commonly +used by pdf producers/readers to show it as a tooltip when field + area +is hovered by a pointing device (eg. mouse).

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+

Since: 0.88

+
+
+
+

poppler_form_field_get_field_type ()

+
PopplerFormFieldType
+poppler_form_field_get_field_type (PopplerFormField *field);
+

Gets the type of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

PopplerFormFieldType of field +

+
+
+
+
+

poppler_form_field_get_font_size ()

+
gdouble
+poppler_form_field_get_font_size (PopplerFormField *field);
+

Gets the font size of field +

+

WARNING: This function always returns 0. Contact the poppler +mailing list if you're interested in implementing it properly

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

the font size of field +

+
+
+
+
+

poppler_form_field_get_id ()

+
gint
+poppler_form_field_get_id (PopplerFormField *field);
+

Gets the id of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

the id of field +

+
+
+
+
+

poppler_form_field_get_mapping_name ()

+
gchar *
+poppler_form_field_get_mapping_name (PopplerFormField *field);
+

Gets the mapping name of field + that is used when +exporting interactive form field data from the document

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+

Since: 0.16

+
+
+
+

poppler_form_field_get_name ()

+
gchar *
+poppler_form_field_get_name (PopplerFormField *field);
+

Gets the fully qualified name of field +. It's constructed by concatenating +the partial field names of the field and all of its ancestors.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+

Since: 0.16

+
+
+
+

poppler_form_field_get_partial_name ()

+
gchar *
+poppler_form_field_get_partial_name (PopplerFormField *field);
+

Gets the partial name of field +.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+

Since: 0.16

+
+
+
+

poppler_form_field_is_read_only ()

+
gboolean
+poppler_form_field_is_read_only (PopplerFormField *field);
+

Checks whether field + is read only

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if field +is read only

+
+
+
+
+

poppler_form_field_signature_validate_async ()

+
void
+poppler_form_field_signature_validate_async
+                               (PopplerFormField *field,
+                                PopplerSignatureValidationFlags flags,
+                                GCancellable *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data);
+

Asynchronously validates the cryptographic signature contained in signature_field +.

+
+

Parameters

+
+++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

field

a PopplerFormField that represents a signature annotation

 

flags

PopplerSignatureValidationFlags flags influencing process of validation of the field signature

 

cancellable

optional GCancellable object.

[nullable]

callback

a GAsyncReadyCallback to call when the signature is validated.

[scope async][closure user_data]

user_data

the data to pass to callback function

 
+
+

Since: 21.12.0

+
+
+
+

poppler_form_field_signature_validate_finish ()

+
PopplerSignatureInfo *
+poppler_form_field_signature_validate_finish
+                               (PopplerFormField *field,
+                                GAsyncResult *result,
+                                GError **error);
+

Finishes validation of the cryptographic signature contained in signature_field +. +See poppler_form_field_signature_validate_async().

+
+

Parameters

+
+++++ + + + + + + + + + + + + + + + + + +

field

a PopplerFormField that represents a signature annotation

 

result

a GAsyncResult

 

error

a GError

 
+
+
+

Returns

+

a PopplerSignatureInfo structure containing signature metadata and validation status +Free the returned structure with poppler_signature_info_free().

+

[transfer full]

+
+

Since: 21.12.0

+
+
+
+

poppler_form_field_signature_validate_sync ()

+
PopplerSignatureInfo *
+poppler_form_field_signature_validate_sync
+                               (PopplerFormField *field,
+                                PopplerSignatureValidationFlags flags,
+                                GCancellable *cancellable,
+                                GError **error);
+

Synchronously validates the cryptographic signature contained in signature_field +.

+
+

Parameters

+
+++++ + + + + + + + + + + + + + + + + + + + + + + +

field

a PopplerFormField that represents a signature annotation

 

flags

PopplerSignatureValidationFlags flags influencing process of validation of the field signature

 

cancellable

optional GCancellable object.

[nullable]

error

a GError

 
+
+
+

Returns

+

a PopplerSignatureInfo structure containing signature metadata and validation status +Free the returned structure with poppler_signature_info_free().

+

[transfer full]

+
+

Since: 21.12.0

+
+
+
+

poppler_form_field_text_do_scroll ()

+
gboolean
+poppler_form_field_text_do_scroll (PopplerFormField *field);
+
+
+
+

poppler_form_field_text_do_spell_check ()

+
gboolean
+poppler_form_field_text_do_spell_check
+                               (PopplerFormField *field);
+

Checks whether spell checking should be done for the contents of field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if spell checking should be done for field +

+
+
+
+
+

poppler_form_field_text_get_max_len ()

+
gint
+poppler_form_field_text_get_max_len (PopplerFormField *field);
+

Retrieves the maximum allowed length of the text in field +

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

the maximum allowed number of characters in field +, or -1 if there is no maximum.

+
+
+
+
+

poppler_form_field_text_get_text ()

+
gchar *
+poppler_form_field_text_get_text (PopplerFormField *field);
+

Retrieves the contents of field +.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

a new allocated string. It must be freed with g_free() when done.

+
+
+
+
+

poppler_form_field_text_get_text_type ()

+
PopplerFormTextType
+poppler_form_field_text_get_text_type (PopplerFormField *field);
+

Gets the text type of field +.

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

PopplerFormTextType of field +

+
+
+
+
+

poppler_form_field_text_is_password ()

+
gboolean
+poppler_form_field_text_is_password (PopplerFormField *field);
+

Checks whether content of field + is a password and it must be hidden

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if the content of field +is a password

+
+
+
+
+

poppler_form_field_text_is_rich_text ()

+
gboolean
+poppler_form_field_text_is_rich_text (PopplerFormField *field);
+

Checks whether the contents of field + are rich text

+
+

Parameters

+
+++++ + + + + + +

field

a PopplerFormField

 
+
+
+

Returns

+

TRUE if the contents of field +are rich text

+
+
+
+
+

poppler_form_field_text_set_text ()

+
void
+poppler_form_field_text_set_text (PopplerFormField *field,
+                                  const gchar *text);
+

Sets the text in field + to the given value, replacing the current contents.

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

field

a PopplerFormField

 

text

the new text

 
+
+
+
+
+

poppler_signature_info_copy ()

+
PopplerSignatureInfo *
+poppler_signature_info_copy (const PopplerSignatureInfo *siginfo);
+

Copies siginfo +, creating an identical PopplerSignatureInfo.

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo structure containing signature metadata and validation status

 
+
+
+

Returns

+

a new PopplerSignatureInfo structure identical to siginfo +.

+

[transfer full]

+
+

Since: 21.12.0

+
+
+
+

poppler_signature_info_free ()

+
void
+poppler_signature_info_free (PopplerSignatureInfo *siginfo);
+

Frees siginfo +

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo structure containing signature metadata and validation status

 
+
+

Since: 21.12.0

+
+
+
+

poppler_signature_info_get_certificate_info ()

+
PopplerCertificateInfo *
+poppler_signature_info_get_certificate_info
+                               (const PopplerSignatureInfo *siginfo);
+

Returns PopplerCertificateInfo for given PopplerSignatureInfo.

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo

 
+
+
+

Returns

+

certificate info of the signature.

+

[transfer none]

+
+

Since: 23.08.0

+
+
+
+

poppler_signature_info_get_certificate_status ()

+
PopplerCertificateStatus
+poppler_signature_info_get_certificate_status
+                               (const PopplerSignatureInfo *siginfo);
+

Returns status of the certificate for given PopplerSignatureInfo.

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo

 
+
+
+

Returns

+

certificate status of the signature

+
+

Since: 21.12.0

+
+
+
+

poppler_signature_info_get_signature_status ()

+
PopplerSignatureStatus
+poppler_signature_info_get_signature_status
+                               (const PopplerSignatureInfo *siginfo);
+

Returns status of the signature for given PopplerSignatureInfo.

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo

 
+
+
+

Returns

+

signature status of the signature

+
+

Since: 21.12.0

+
+
+
+

poppler_signature_info_get_signer_name ()

+
const gchar *
+poppler_signature_info_get_signer_name
+                               (const PopplerSignatureInfo *siginfo);
+

Returns name of signer for given PopplerSignatureInfo.

+
+

Parameters

+
++ - - + +

field

a PopplerFormField

siginfo

a PopplerSignatureInfo

 
-

Returns

-

TRUE if field -allows multiple choices to be selected

+

Returns

+

A string.

+

[transfer none]

+

Since: 21.12.0


-

poppler_form_field_choice_commit_on_change ()

-
gboolean
-poppler_form_field_choice_commit_on_change
-                               (PopplerFormField *field);
+

poppler_signature_info_get_local_signing_time ()

+
GDateTime *
+poppler_signature_info_get_local_signing_time
+                               (const PopplerSignatureInfo *siginfo);
+

Returns local time of signing as GDateTime. This does not +contain information about time zone since it has not been +preserved during conversion. +Do not modify returned value since it is internal to +PopplerSignatureInfo.

+
+

Parameters

+
+++++ + + + + + +

siginfo

a PopplerSignatureInfo

 
+
+
+

Returns

+

GDateTime.

+

[transfer none]

+
+

Since: 21.12.0


-

poppler_form_field_choice_do_spell_check ()

-
gboolean
-poppler_form_field_choice_do_spell_check
-                               (PopplerFormField *field);
-

Checks whether spell checking should be done for the contents of field -

+

poppler_signing_data_new ()

+
PopplerSigningData *
+poppler_signing_data_new (void);
+

Creates a new PopplerSigningData with default content.

-

Parameters

+

Returns

+

a new PopplerSigningData. It must be freed with poppler_signing_data_free() when done.

+
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_copy ()

+
PopplerSigningData *
+poppler_signing_data_copy (const PopplerSigningData *signing_data);
+

Copies signing_data +, creating an identical PopplerSigningData.

+
+

Parameters

@@ -607,28 +2472,29 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

TRUE if spell checking should be done for field -

+

Returns

+

a new PopplerSigningData structure identical to signing_data +.

+

[transfer full]

+

Since: 23.07.0


-

poppler_form_field_choice_get_choice_type ()

-
PopplerFormChoiceType
-poppler_form_field_choice_get_choice_type
-                               (PopplerFormField *field);
-

Gets the choice type of field +

poppler_signing_data_free ()

+
void
+poppler_signing_data_free (PopplerSigningData *signing_data);
+

Frees signing_data

-

Parameters

+

Parameters

@@ -636,28 +2502,56 @@

Poppl

- - - + + +

field

a PopplerFormField

 

signing_data

a PopplerSigningData structure containing signing data.

[nullable]
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_destination_filename ()

+
void
+poppler_signing_data_set_destination_filename
+                               (PopplerSigningData *signing_data,
+                                const gchar *filename);
+

Set destination file name.

-

Returns

-

PopplerFormChoiceType of field -

+

Parameters

+
+++++ + + + + + + + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

filename

destination filename

 
+

Since: 23.07.0


-

poppler_form_field_choice_get_item ()

-
gchar *
-poppler_form_field_choice_get_item (PopplerFormField *field,
-                                    gint index);
-

Returns the contents of the item on field - at the given index

+

poppler_signing_data_set_certificate_info ()

+
void
+poppler_signing_data_set_certificate_info
+                               (PopplerSigningData *signing_data,
+                                const PopplerCertificateInfo *certificate_info);
+

Set certification information.

-

Parameters

+

Parameters

@@ -666,117 +2560,281 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

index

the index of the item

certificate_info

a PopplerCertificateInfo

 
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_page ()

+
void
+poppler_signing_data_set_page (PopplerSigningData *signing_data,
+                               int page);
+

Set page (>=0).

-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Parameters

+
+++++ + + + + + + + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

page

a page number

 
+

Since: 23.07.0


-

poppler_form_field_choice_get_n_items ()

-
gint
-poppler_form_field_choice_get_n_items (PopplerFormField *field);
-

Returns the number of items on field -

+

poppler_signing_data_set_signature_text ()

+
void
+poppler_signing_data_set_signature_text
+                               (PopplerSigningData *signing_data,
+                                const gchar *signature_text);
+

Set signature text.

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

signature_text

text to show as main signature

 
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_signature_text_left ()

+
void
+poppler_signing_data_set_signature_text_left
+                               (PopplerSigningData *signing_data,
+                                const gchar *signature_text_left);
+

Set small signature text on the left hand.

-

Returns

-

the number of items on field -

+

Parameters

+
+++++ + + + + + + + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

signature_text_left

text to show as small left signature

 
+

Since: 23.07.0


-

poppler_form_field_choice_get_text ()

-
gchar *
-poppler_form_field_choice_get_text (PopplerFormField *field);
-

Retrieves the contents of field -.

+

poppler_signing_data_set_signature_rectangle ()

+
void
+poppler_signing_data_set_signature_rectangle
+                               (PopplerSigningData *signing_data,
+                                const PopplerRectangle *signature_rect);
+

Set signature rectangle.

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

signature_rect

a PopplerRectangle where signature should be shown

 
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_font_color ()

+
void
+poppler_signing_data_set_font_color (PopplerSigningData *signing_data,
+                                     const PopplerColor *font_color);
+

Set signature font color.

-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Parameters

+
+++++ + + + + + + + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

font_color

a PopplerColor to be used as signature font color

 
+

Since: 23.07.0


-

poppler_form_field_choice_is_editable ()

-
gboolean
-poppler_form_field_choice_is_editable (PopplerFormField *field);
-

Checks whether field - is editable

+

poppler_signing_data_set_font_size ()

+
void
+poppler_signing_data_set_font_size (PopplerSigningData *signing_data,
+                                    gdouble font_size);
+

Set signature font size (>0).

-

Parameters

+

Parameters

- - - + + + + + + + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

font_size

signature font size

 
+
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_left_font_size ()

+
void
+poppler_signing_data_set_left_font_size
+                               (PopplerSigningData *signing_data,
+                                gdouble font_size);
+

Set signature left font size (> 0).

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

font_size

signature font size

 
+
+

Since: 23.07.0

+
+
+
+

poppler_signing_data_set_border_color ()

+
void
+poppler_signing_data_set_border_color (PopplerSigningData *signing_data,
+                                       const PopplerColor *border_color);
+

Set signature border color.

+
+

Parameters

+
+++++ + + + + - + + + + + + +

signing_data

a PopplerSigningData structure containing signing data

 

border_color

a PopplerColor to be used for signature border

 
-
-

Returns

-

TRUE if field -is editable

-
+

Since: 23.07.0


-

poppler_form_field_choice_is_item_selected ()

-
gboolean
-poppler_form_field_choice_is_item_selected
-                               (PopplerFormField *field,
-                                gint index);
-

Checks whether the item at the given index on field - is currently selected

+

poppler_signing_data_set_border_width ()

+
void
+poppler_signing_data_set_border_width (PopplerSigningData *signing_data,
+                                       gdouble border_width);
+

Set signature border width.

-

Parameters

+

Parameters

@@ -785,34 +2843,30 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

index

the index of the item

border_width

border width

 
-
-

Returns

-

TRUE if item at index -is currently selected

-
+

Since: 23.07.0


-

poppler_form_field_choice_select_item ()

+

poppler_signing_data_set_background_color ()

void
-poppler_form_field_choice_select_item (PopplerFormField *field,
-                                       gint index);
-

Selects the item at the given index on field -

+poppler_signing_data_set_background_color + (PopplerSigningData *signing_data, + const PopplerColor *background_color);
+

Set signature background color.

-

Parameters

+

Parameters

@@ -821,29 +2875,30 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

index

the index of the item

background_color

a PopplerColor to be used for signature background

 
+

Since: 23.07.0


-

poppler_form_field_choice_set_text ()

+

poppler_signing_data_set_field_partial_name ()

void
-poppler_form_field_choice_set_text (PopplerFormField *field,
-                                    const gchar *text);
-

Sets the text in field - to the given value, replacing the current contents

+poppler_signing_data_set_field_partial_name + (PopplerSigningData *signing_data, + const gchar *field_partial_name); +

Set field partial name (existing field id or a new one) where signature is placed.

-

Parameters

+

Parameters

@@ -852,28 +2907,29 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

text

the new text

field_partial_name

a field partial name

 
+

Since: 23.07.0


-

poppler_form_field_choice_toggle_item ()

+

poppler_signing_data_set_reason ()

void
-poppler_form_field_choice_toggle_item (PopplerFormField *field,
-                                       gint index);
-

Changes the state of the item at the given index

+poppler_signing_data_set_reason (PopplerSigningData *signing_data, + const gchar *reason); +

Set reason for signature (e.g. I'm approver).

-

Parameters

+

Parameters

@@ -882,87 +2938,91 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

index

the index of the item

reason

a reason

 
+

Since: 23.07.0


-

poppler_form_field_choice_unselect_all ()

+

poppler_signing_data_set_location ()

void
-poppler_form_field_choice_unselect_all
-                               (PopplerFormField *field);
-

Unselects all the items on field -

+poppler_signing_data_set_location (PopplerSigningData *signing_data, + const gchar *location); +

Set signature location (e.g. "At my desk").

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

location

a location

 
+

Since: 23.07.0


-

poppler_form_field_get_action ()

-
PopplerAction *
-poppler_form_field_get_action (PopplerFormField *field);
-

Retrieves the action (PopplerAction) that shall be -performed when field - is activated, or NULL

+

poppler_signing_data_set_image_path ()

+
void
+poppler_signing_data_set_image_path (PopplerSigningData *signing_data,
+                                     const gchar *image_path);
+

Set signature background (watermark) image path.

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

image_path

signature image path

 
-
-

Returns

-

the action to perform. The returned -object is owned by field -and should not be freed.

-

[transfer none]

-
-

Since: 0.18

+

Since: 23.07.0


-

poppler_form_field_get_additional_action ()

-
PopplerAction *
-poppler_form_field_get_additional_action
-                               (PopplerFormField *field,
-                                PopplerAdditionalActionType type);
-

Retrieves the action (PopplerAction) that shall be performed when -an additional action is triggered on field -, or NULL.

+

poppler_signing_data_set_password ()

+
void
+poppler_signing_data_set_password (PopplerSigningData *signing_data,
+                                   const gchar *password);
+

Set password for the signing key.

-

Parameters

+

Parameters

@@ -971,98 +3031,93 @@

Poppl

- - + + - - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

type

the type of additional action

password

a password

 
-
-

Returns

-

the action to perform. The returned -object is owned by field -and should not be freed.

-

[transfer none]

-
-

Since: 0.72

+

Since: 23.07.0


-

poppler_form_field_get_alternate_ui_name ()

-
gchar *
-poppler_form_field_get_alternate_ui_name
-                               (PopplerFormField *field);
-

Gets the alternate ui name of field -. This name is also commonly -used by pdf producers/readers to show it as a tooltip when field - area -is hovered by a pointing device (eg. mouse).

+

poppler_signing_data_set_document_owner_password ()

+
void
+poppler_signing_data_set_document_owner_password
+                               (PopplerSigningData *signing_data,
+                                const gchar *document_owner_password);
+

Set document owner password (for encrypted files).

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

document_owner_password

document owner password

 
-
-

Returns

-

a new allocated string. It must be freed with g_free() when done.

-
-

Since: 0.88

+

Since: 23.07.0


-

poppler_form_field_get_field_type ()

-
PopplerFormFieldType
-poppler_form_field_get_field_type (PopplerFormField *field);
-

Gets the type of field -

+

poppler_signing_data_set_document_user_password ()

+
void
+poppler_signing_data_set_document_user_password
+                               (PopplerSigningData *signing_data,
+                                const gchar *document_user_password);
+

Set document user password (for encrypted files).

-

Parameters

+

Parameters

- - - + + + + - + + + + + + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 

document_user_password

document user password

 
-
-

Returns

-

PopplerFormFieldType of field -

-
+

Since: 23.07.0


-

poppler_form_field_get_font_size ()

-
gdouble
-poppler_form_field_get_font_size (PopplerFormField *field);
-

Gets the font size of field -

-

WARNING: This function always returns 0. Contact the poppler -mailing list if you're interested in implementing it properly

+

poppler_signing_data_get_destination_filename ()

+
const gchar *
+poppler_signing_data_get_destination_filename
+                               (const PopplerSigningData *signing_data);
+

Get destination file name.

-

Parameters

+

Parameters

@@ -1070,27 +3125,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

the font size of field -

+

Returns

+

destination filename

+

Since: 23.07.0


-

poppler_form_field_get_id ()

-
gint
-poppler_form_field_get_id (PopplerFormField *field);
-

Gets the id of field -

+

poppler_signing_data_get_certificate_info ()

+
const PopplerCertificateInfo *
+poppler_signing_data_get_certificate_info
+                               (const PopplerSigningData *signing_data);
+

Get certification information.

-

Parameters

+

Parameters

@@ -1098,28 +3153,26 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

the id of field -

+

Returns

+

a PopplerCertificateInfo

+

Since: 23.07.0


-

poppler_form_field_get_mapping_name ()

-
gchar *
-poppler_form_field_get_mapping_name (PopplerFormField *field);
-

Gets the mapping name of field - that is used when -exporting interactive form field data from the document

+

poppler_signing_data_get_page ()

+
int
+poppler_signing_data_get_page (const PopplerSigningData *signing_data);
+

Get page.

-

Parameters

+

Parameters

@@ -1127,28 +3180,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Returns

+

page number

-

Since: 0.16

+

Since: 23.07.0


-

poppler_form_field_get_name ()

-
gchar *
-poppler_form_field_get_name (PopplerFormField *field);
-

Gets the fully qualified name of field -. It's constructed by concatenating -the partial field names of the field and all of its ancestors.

+

poppler_signing_data_get_signature_text ()

+
const gchar *
+poppler_signing_data_get_signature_text
+                               (const PopplerSigningData *signing_data);
+

Get signature text.

-

Parameters

+

Parameters

@@ -1156,27 +3208,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Returns

+

signature text

-

Since: 0.16

+

Since: 23.07.0


-

poppler_form_field_get_partial_name ()

-
gchar *
-poppler_form_field_get_partial_name (PopplerFormField *field);
-

Gets the partial name of field -.

+

poppler_signing_data_get_signature_text_left ()

+
const gchar *
+poppler_signing_data_get_signature_text_left
+                               (const PopplerSigningData *signing_data);
+

Get signature text left.

-

Parameters

+

Parameters

@@ -1184,27 +3236,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Returns

+

signature text left

-

Since: 0.16

+

Since: 23.07.0


-

poppler_form_field_is_read_only ()

-
gboolean
-poppler_form_field_is_read_only (PopplerFormField *field);
-

Checks whether field - is read only

+

poppler_signing_data_get_signature_rectangle ()

+
const PopplerRectangle *
+poppler_signing_data_get_signature_rectangle
+                               (const PopplerSigningData *signing_data);
+

Get signature rectangle.

-

Parameters

+

Parameters

@@ -1212,182 +3264,108 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

TRUE if field -is read only

+

Returns

+

a PopplerRectangle

+

Since: 23.07.0


-

poppler_form_field_signature_validate_async ()

-
void
-poppler_form_field_signature_validate_async
-                               (PopplerFormField *field,
-                                PopplerSignatureValidationFlags flags,
-                                GCancellable *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer user_data);
-

Asynchronously validates the cryptographic signature contained in signature_field -.

+

poppler_signing_data_get_font_color ()

+
const PopplerColor *
+poppler_signing_data_get_font_color (const PopplerSigningData *signing_data);
+

Get signature font color.

-

Parameters

+

Parameters

--- - - - - - - - - - - - - - - - - - - - - - - - - - - - +++ + + + + +

field

a PopplerFormField that represents a signature annotation

 

flags

PopplerSignatureValidationFlags flags influencing process of validation of the field signature

 

cancellable

optional GCancellable object.

[nullable]

callback

a GAsyncReadyCallback to call when the signature is validated.

[scope async]

user_data

the data to pass to callback function.

[closure]

signing_data

a PopplerSigningData structure containing signing data

 
-

Since: 21.12.0

+
+

Returns

+

a PopplerColor

+
+

Since: 23.07.0


-

poppler_form_field_signature_validate_finish ()

-
PopplerSignatureInfo *
-poppler_form_field_signature_validate_finish
-                               (PopplerFormField *field,
-                                GAsyncResult *result,
-                                GError **error);
-

Finishes validation of the cryptographic signature contained in signature_field -. -See poppler_form_field_signature_validate_async().

+

poppler_signing_data_get_font_size ()

+
gdouble
+poppler_signing_data_get_font_size (const PopplerSigningData *signing_data);
+

Get signature font size.

-

Parameters

+

Parameters

- - - - - - - - - - - - - - + + + - - +

field

a PopplerFormField that represents a signature annotation

 

result

a GAsyncResult

 

error

a GError

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a PopplerSignatureInfo structure containing signature metadata and validation status -Free the returned structure with poppler_signature_info_free().

-

[transfer full]

+

Returns

+

font size

-

Since: 21.12.0

+

Since: 23.07.0


-

poppler_form_field_signature_validate_sync ()

-
PopplerSignatureInfo *
-poppler_form_field_signature_validate_sync
-                               (PopplerFormField *field,
-                                PopplerSignatureValidationFlags flags,
-                                GCancellable *cancellable,
-                                GError **error);
-

Synchronously validates the cryptographic signature contained in signature_field -.

+

poppler_signing_data_get_left_font_size ()

+
gdouble
+poppler_signing_data_get_left_font_size
+                               (const PopplerSigningData *signing_data);
+

Get signature left font size.

-

Parameters

+

Parameters

- - - - - - - - - - - - - - - - - - - + + + - - +

field

a PopplerFormField that represents a signature annotation

 

flags

PopplerSignatureValidationFlags flags influencing process of validation of the field signature

 

cancellable

optional GCancellable object.

[nullable]

error

a GError

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a PopplerSignatureInfo structure containing signature metadata and validation status -Free the returned structure with poppler_signature_info_free().

-

[transfer full]

-
-

Since: 21.12.0

+

Returns

+

left font size

-
-
-

poppler_form_field_text_do_scroll ()

-
gboolean
-poppler_form_field_text_do_scroll (PopplerFormField *field);
+

Since: 23.07.0


-

poppler_form_field_text_do_spell_check ()

-
gboolean
-poppler_form_field_text_do_spell_check
-                               (PopplerFormField *field);
-

Checks whether spell checking should be done for the contents of field -

+

poppler_signing_data_get_border_color ()

+
const PopplerColor *
+poppler_signing_data_get_border_color (const PopplerSigningData *signing_data);
+

Get signature border color.

-

Parameters

+

Parameters

@@ -1395,27 +3373,26 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

TRUE if spell checking should be done for field -

+

Returns

+

a PopplerColor

+

Since: 23.07.0


-

poppler_form_field_text_get_max_len ()

-
gint
-poppler_form_field_text_get_max_len (PopplerFormField *field);
-

Retrieves the maximum allowed length of the text in field -

+

poppler_signing_data_get_border_width ()

+
gdouble
+poppler_signing_data_get_border_width (const PopplerSigningData *signing_data);
+

Get signature border width.

-

Parameters

+

Parameters

@@ -1423,27 +3400,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

the maximum allowed number of characters in field -, or -1 if there is no maximum.

+

Returns

+

border width

+

Since: 23.07.0


-

poppler_form_field_text_get_text ()

-
gchar *
-poppler_form_field_text_get_text (PopplerFormField *field);
-

Retrieves the contents of field -.

+

poppler_signing_data_get_background_color ()

+
const PopplerColor *
+poppler_signing_data_get_background_color
+                               (const PopplerSigningData *signing_data);
+

Get signature background color.

-

Parameters

+

Parameters

@@ -1451,26 +3428,27 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a new allocated string. It must be freed with g_free() when done.

+

Returns

+

a PopplerColor

+

Since: 23.07.0


-

poppler_form_field_text_get_text_type ()

-
PopplerFormTextType
-poppler_form_field_text_get_text_type (PopplerFormField *field);
-

Gets the text type of field -.

+

poppler_signing_data_get_field_partial_name ()

+
const gchar *
+poppler_signing_data_get_field_partial_name
+                               (const PopplerSigningData *signing_data);
+

Get field partial name.

-

Parameters

+

Parameters

@@ -1478,27 +3456,26 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

PopplerFormTextType of field -

+

Returns

+

field partial name

+

Since: 23.07.0


-

poppler_form_field_text_is_password ()

-
gboolean
-poppler_form_field_text_is_password (PopplerFormField *field);
-

Checks whether content of field - is a password and it must be hidden

+

poppler_signing_data_get_reason ()

+
const gchar *
+poppler_signing_data_get_reason (const PopplerSigningData *signing_data);
+

Get reason.

-

Parameters

+

Parameters

@@ -1506,27 +3483,26 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

TRUE if the content of field -is a password

+

Returns

+

reason

+

Since: 23.07.0


-

poppler_form_field_text_is_rich_text ()

-
gboolean
-poppler_form_field_text_is_rich_text (PopplerFormField *field);
-

Checks whether the contents of field - are rich text

+

poppler_signing_data_get_location ()

+
const gchar *
+poppler_signing_data_get_location (const PopplerSigningData *signing_data);
+

Get location.

-

Parameters

+

Parameters

@@ -1534,58 +3510,53 @@

Poppl

- - + +

field

a PopplerFormField

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

TRUE if the contents of field -are rich text

+

Returns

+

location

+

Since: 23.07.0


-

poppler_form_field_text_set_text ()

-
void
-poppler_form_field_text_set_text (PopplerFormField *field,
-                                  const gchar *text);
-

Sets the text in field - to the given value, replacing the current contents.

+

poppler_signing_data_get_image_path ()

+
const gchar *
+poppler_signing_data_get_image_path (const PopplerSigningData *signing_data);
+

Get image path.

-

Parameters

+

Parameters

- - - - - - - - - + + + - - +

field

a PopplerFormField

 

text

the new text

signing_data

a PopplerSigningData structure containing signing data

 
+
+

Returns

+

image path

+
+

Since: 23.07.0


-

poppler_signature_info_copy ()

-
PopplerSignatureInfo *
-poppler_signature_info_copy (const PopplerSignatureInfo *siginfo);
-

Copies siginfo -, creating an identical PopplerSignatureInfo.

+

poppler_signing_data_get_password ()

+
const gchar *
+poppler_signing_data_get_password (const PopplerSigningData *signing_data);
+

Get signing key password.

-

Parameters

+

Parameters

@@ -1593,29 +3564,27 @@

Poppl

- - + +

siginfo

a PopplerSignatureInfo structure containing signature metadata and validation status

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

a new PopplerSignatureInfo structure identical to siginfo -.

-

[transfer full]

+

Returns

+

password

-

Since: 21.12.0

+

Since: 23.07.0


-

poppler_signature_info_free ()

-
void
-poppler_signature_info_free (PopplerSignatureInfo *siginfo);
-

Frees siginfo -

+

poppler_signing_data_get_document_owner_password ()

+
const gchar *
+poppler_signing_data_get_document_owner_password
+                               (const PopplerSigningData *signing_data);
+

Get document owner password.

-

Parameters

+

Parameters

@@ -1623,23 +3592,27 @@

Poppl

- - + +

siginfo

a PopplerSignatureInfo structure containing signature metadata and validation status

signing_data

a PopplerSigningData structure containing signing data

 
-

Since: 21.12.0

+
+

Returns

+

document owner password (for encrypted files)

+
+

Since: 23.07.0


-

poppler_signature_info_get_certificate_status ()

-
PopplerCertificateStatus
-poppler_signature_info_get_certificate_status
-                               (const PopplerSignatureInfo *siginfo);
-

Returns status of the certificate for given PopplerSignatureInfo.

+

poppler_signing_data_get_document_user_password ()

+
const gchar *
+poppler_signing_data_get_document_user_password
+                               (const PopplerSigningData *signing_data);
+

Get document user password.

-

Parameters

+

Parameters

@@ -1647,27 +3620,39 @@

Poppl

- - + +

siginfo

a PopplerSignatureInfo

signing_data

a PopplerSigningData structure containing signing data

 
-

Returns

-

certificate status of the signature

+

Returns

+

document user password (for encrypted files)

-

Since: 21.12.0

+

Since: 23.07.0


-

poppler_signature_info_get_signature_status ()

-
PopplerSignatureStatus
-poppler_signature_info_get_signature_status
-                               (const PopplerSignatureInfo *siginfo);
-

Returns status of the signature for given PopplerSignatureInfo.

+

poppler_certificate_info_new ()

+
PopplerCertificateInfo *
+poppler_certificate_info_new (void);
+

Creates a new PopplerCertificateInfo

-

Parameters

+

Returns

+

a new PopplerCertificateInfo. It must be freed with poppler_certificate_info_free() when done.

+
+

Since: 23.07.0

+
+
+
+

poppler_certificate_info_copy ()

+
PopplerCertificateInfo *
+poppler_certificate_info_copy (const PopplerCertificateInfo *certificate_info);
+

Copies certificate_info +, creating an identical PopplerCertificateInfo.

+
+

Parameters

@@ -1675,27 +3660,49 @@

Poppl

- - + +

siginfo

a PopplerSignatureInfo

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
-

Returns

-

signature status of the signature

+

Returns

+

a new PopplerCertificateInfo structure identical to certificate_info +.

+

[transfer full]

-

Since: 21.12.0

+

Since: 23.07.0


-

poppler_signature_info_get_signer_name ()

-
const gchar *
-poppler_signature_info_get_signer_name
-                               (const PopplerSignatureInfo *siginfo);
-

Returns name of signer for given PopplerSignatureInfo.

+

poppler_set_nss_dir ()

+
void
+poppler_set_nss_dir (const char *path);
+

Set NSS directory

+

Since: 23.07.0

+
+
+
+

poppler_get_nss_dir ()

+
char *
+poppler_get_nss_dir (void);
+

Get NSS directory

-

Parameters

+

Returns

+

nss directroy.

+

[transfer full]

+
+

Since: 23.07.0

+
+
+
+

poppler_set_nss_password_callback ()

+
void
+poppler_set_nss_password_callback (PopplerNssPasswordFunc func);
+

A callback which asks for certificate password

+
+

Parameters

@@ -1703,32 +3710,36 @@

Poppl

- - - + + +

siginfo

a PopplerSignatureInfo

 

func

a PopplerNssPasswordFunc that represents a signature annotation.

[scope call]
+

Since: 23.07.0

+
+
+
+

poppler_get_available_signing_certificates ()

+
GList *
+poppler_get_available_signing_certificates
+                               (void);
+

Get all available signing certificate information

-

Returns

-

A string.

-

[transfer none]

+

Returns

+

all available signing certificate information.

+

[transfer full][element-type PopplerCertificateInfo]

-

Since: 21.12.0


-

poppler_signature_info_get_local_signing_time ()

-
GDateTime *
-poppler_signature_info_get_local_signing_time
-                               (const PopplerSignatureInfo *siginfo);
-

Returns local time of signing as GDateTime. This does not -contain information about time zone since it has not been -preserved during conversion. -Do not modify returned value since it is internal to -PopplerSignatureInfo.

+

poppler_certificate_info_free ()

+
void
+poppler_certificate_info_free (PopplerCertificateInfo *certificate_info);
+

Frees certificate_info +

-

Parameters

+

Parameters

@@ -1736,18 +3747,13 @@

Poppl

- - + +

siginfo

a PopplerSignatureInfo

certificate_info

a PopplerCertificateInfo structure containing certificate information

 
-
-

Returns

-

GDateTime.

-

[transfer none]

-
-

Since: 21.12.0

+

Since: 23.07.0

@@ -1807,6 +3813,14 @@

Poppl


+

PopplerCertificateInfo

+
typedef struct _PopplerCertificateInfo PopplerCertificateInfo;
+

PopplerCertificateInfo contains detailed info about a signing certificate.

+

Since 24.10 this type supports g_autoptr

+

Since: 23.07.0

+
+
+

enum PopplerCertificateStatus

Signature certificate verification results

@@ -2007,6 +4021,7 @@

Poppl
typedef struct _PopplerSignatureInfo PopplerSignatureInfo;

PopplerSignatureInfo contains detailed info about a signature contained in a form field.

+

Since 24.10 this type supports g_autoptr

Since: 21.12.0


@@ -2118,6 +4133,6 @@

Poppl

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/PopplerStructureElement.html b/glib/reference/html/PopplerStructureElement.html index 18b6212..06df7c4 100644 --- a/glib/reference/html/PopplerStructureElement.html +++ b/glib/reference/html/PopplerStructureElement.html @@ -8,7 +8,7 @@ - + @@ -42,7 +42,7 @@

-gchar * +gchar * poppler_structure_element_get_abbreviation () @@ -50,7 +50,7 @@

-gchar * +gchar * poppler_structure_element_get_actual_text () @@ -58,7 +58,7 @@

-gchar * +gchar * poppler_structure_element_get_alt_text () @@ -66,7 +66,7 @@

-gboolean +gboolean poppler_structure_element_get_background_color () @@ -74,7 +74,7 @@

-gdouble +gdouble poppler_structure_element_get_baseline_shift () @@ -90,7 +90,7 @@

-gboolean +gboolean poppler_structure_element_get_border_color () @@ -106,7 +106,7 @@

-gboolean +gboolean poppler_structure_element_get_border_thickness () @@ -114,7 +114,7 @@

-gboolean +gboolean poppler_structure_element_get_bounding_box () @@ -122,7 +122,7 @@

-gboolean +gboolean poppler_structure_element_get_color () @@ -130,7 +130,7 @@

-guint +guint poppler_structure_element_get_column_count () @@ -138,7 +138,7 @@

-gdouble * +gdouble * poppler_structure_element_get_column_gaps () @@ -146,7 +146,7 @@

-gdouble * +gdouble * poppler_structure_element_get_column_widths () @@ -154,7 +154,7 @@

-gdouble +gdouble poppler_structure_element_get_end_indent () @@ -162,7 +162,7 @@

-gchar * +gchar * poppler_structure_element_get_form_description () @@ -194,7 +194,7 @@

-gdouble +gdouble poppler_structure_element_get_height () @@ -202,7 +202,7 @@

-gchar * +gchar * poppler_structure_element_get_id () @@ -226,7 +226,7 @@

-gchar * +gchar * poppler_structure_element_get_language () @@ -234,7 +234,7 @@

-gdouble +gdouble poppler_structure_element_get_line_height () @@ -258,7 +258,7 @@

-gint +gint poppler_structure_element_get_page () @@ -290,7 +290,7 @@

-gdouble +gdouble poppler_structure_element_get_space_after () @@ -298,7 +298,7 @@

-gdouble +gdouble poppler_structure_element_get_space_before () @@ -306,7 +306,7 @@

-gdouble +gdouble poppler_structure_element_get_start_indent () @@ -322,7 +322,7 @@

-guint +guint poppler_structure_element_get_table_column_span () @@ -330,7 +330,7 @@

-gchar ** +gchar ** poppler_structure_element_get_table_headers () @@ -346,7 +346,7 @@

-guint +guint poppler_structure_element_get_table_row_span () @@ -362,7 +362,7 @@

-gchar * +gchar * poppler_structure_element_get_table_summary () @@ -370,7 +370,7 @@

-gchar * +gchar * poppler_structure_element_get_text () @@ -386,7 +386,7 @@

-gboolean +gboolean poppler_structure_element_get_text_decoration_color () @@ -394,7 +394,7 @@

-gdouble +gdouble poppler_structure_element_get_text_decoration_thickness () @@ -410,7 +410,7 @@

-gdouble +gdouble poppler_structure_element_get_text_indent () @@ -426,7 +426,7 @@

-gchar * +gchar * poppler_structure_element_get_title () @@ -434,7 +434,7 @@

-gdouble +gdouble poppler_structure_element_get_width () @@ -450,7 +450,7 @@

-gboolean +gboolean poppler_structure_element_is_block () @@ -458,7 +458,7 @@

-gboolean +gboolean poppler_structure_element_is_content () @@ -466,7 +466,7 @@

-gboolean +gboolean poppler_structure_element_is_grouping () @@ -474,7 +474,7 @@

-gboolean +gboolean poppler_structure_element_is_inline () @@ -522,7 +522,7 @@

-gboolean +gboolean poppler_structure_element_iter_next () @@ -636,7 +636,7 @@

╰── PopplerStructureWritingMode GFlags ╰── PopplerStructureGetTextFlags - GObject + GObject ╰── PopplerStructureElement @@ -657,7 +657,7 @@

Functions

poppler_structure_element_get_abbreviation ()

-
gchar *
+
gchar *
 poppler_structure_element_get_abbreviation
                                (PopplerStructureElement *poppler_structure_element);

Acronyms and abbreviations contained in elements of type @@ -681,7 +681,7 @@

Returns

Text of the expanded abbreviation if the -element text is an abbreviation or acrony, NULL if not.

+element text is an abbreviation or acrony, NULL if not.

[transfer full]

Since: 0.26

@@ -689,7 +689,7 @@

poppler_structure_element_get_actual_text ()

-
gchar *
+
gchar *
 poppler_structure_element_get_actual_text
                                (PopplerStructureElement *poppler_structure_element);

Obtains the actual text enclosed by the element (and its child elements). @@ -716,7 +716,7 @@

Returns

-

The actual text for the element, or NULL +

The actual text for the element, or NULL if not defined.

[transfer full]

@@ -725,7 +725,7 @@

poppler_structure_element_get_alt_text ()

-
gchar *
+
gchar *
 poppler_structure_element_get_alt_text
                                (PopplerStructureElement *poppler_structure_element);

Obtains the “alternate” text representation of the element (and its child @@ -751,7 +751,7 @@

Returns

The alternate text representation for the -element, or NULL if not defined.

+element, or NULL if not defined.

[transfer full]

Since: 0.26

@@ -759,7 +759,7 @@

poppler_structure_element_get_background_color ()

-
gboolean
+
gboolean
 poppler_structure_element_get_background_color
                                (PopplerStructureElement *poppler_structure_element,
                                 PopplerColor *color);
@@ -789,15 +789,15 @@

Returns

-

TRUE if a color is defined for the element, -FALSE otherwise.

+

TRUE if a color is defined for the element, +FALSE otherwise.

Since: 0.26


poppler_structure_element_get_baseline_shift ()

-
gdouble
+
gdouble
 poppler_structure_element_get_baseline_shift
                                (PopplerStructureElement *poppler_structure_element);

Obtains how much the text contained in the inline-level structure element should be shifted, @@ -854,7 +854,7 @@

poppler_structure_element_get_border_color ()

-
gboolean
+
gboolean
 poppler_structure_element_get_border_color
                                (PopplerStructureElement *poppler_structure_element,
                                 PopplerColor *colors);
@@ -889,8 +889,8 @@

Returns

-

TRUE if a color is defined for the element, -FALSE otherwise.

+

TRUE if a color is defined for the element, +FALSE otherwise.

Since: 0.26

@@ -931,10 +931,10 @@

poppler_structure_element_get_border_thickness ()

-
gboolean
+
gboolean
 poppler_structure_element_get_border_thickness
                                (PopplerStructureElement *poppler_structure_element,
-                                gdouble *border_thicknesses);
+ gdouble *border_thicknesses);

Obtains the thickness of the border of an element. The result values are in before-after-start-end ordering (for the typical Western left-to-right writing, that is top-bottom-left-right). @@ -963,15 +963,15 @@

Returns

-

TRUE if the border thickness attribute is defined for -the element, FALSE otherwise.

+

TRUE if the border thickness attribute is defined for +the element, FALSE otherwise.

Since: 0.26


poppler_structure_element_get_bounding_box ()

-
gboolean
+
gboolean
 poppler_structure_element_get_bounding_box
                                (PopplerStructureElement *poppler_structure_element,
                                 PopplerRectangle *bounding_box);
@@ -1000,15 +1000,15 @@

Returns

-

TRUE if a bounding box is defined for the element, -FALSE otherwise.

+

TRUE if a bounding box is defined for the element, +FALSE otherwise.

Since: 0.26


poppler_structure_element_get_color ()

-
gboolean
+
gboolean
 poppler_structure_element_get_color (PopplerStructureElement *poppler_structure_element,
                                      PopplerColor *color);

Obtains the color of the content contained in the element. @@ -1038,15 +1038,15 @@

Returns

-

TRUE if a color is defined for the element, -FALSE otherwise.

+

TRUE if a color is defined for the element, +FALSE otherwise.

Since: 0.26


poppler_structure_element_get_column_count ()

-
guint
+
guint
 poppler_structure_element_get_column_count
                                (PopplerStructureElement *poppler_structure_element);

Obtains the number of columns used to lay out the content contained @@ -1075,21 +1075,21 @@

poppler_structure_element_get_column_gaps ()

-
gdouble *
+
gdouble *
 poppler_structure_element_get_column_gaps
                                (PopplerStructureElement *poppler_structure_element,
-                                guint *n_values);
+ guint *n_values);

Obtains the size of the gaps in between adjacent columns. Returns an array of elements: the first one is the size of the gap in between columns 1 and 2, second is the size between columns 2 and 3, and so on.

-

For elements which use a single column, NULL is returned and n_values +

For elements which use a single column, NULL is returned and n_values is set to zero.

-

If the attribute is undefined, NULL is returned and n_values +

If the attribute is undefined, NULL is returned and n_values is set to a non-zero value.

The array with the results is allocated by the function. When it is -not needed anymore, be sure to call g_free() on it.

+not needed anymore, be sure to call g_free() on it.

Parameters

@@ -1114,7 +1114,7 @@

Returns

-

Array containing the values for the column gaps, or NULL if the +

Array containing the values for the column gaps, or NULL if the array is empty or the attribute is not defined.

[transfer full][array length=n_values][element-type gdouble]

@@ -1123,13 +1123,13 @@

poppler_structure_element_get_column_widths ()

-
gdouble *
+
gdouble *
 poppler_structure_element_get_column_widths
                                (PopplerStructureElement *poppler_structure_element,
-                                guint *n_values);
+ guint *n_values);

Obtains an array with the widths of the columns.

The array with the results is allocated by the function. When it is -not needed anymore, be sure to call g_free() on it.

+not needed anymore, be sure to call g_free() on it.

Parameters

@@ -1154,7 +1154,7 @@

Returns

-

Array containing widths of the columns, or NULL if the attribute +

Array containing widths of the columns, or NULL if the attribute is not defined.

[transfer full][array length=n_values][element-type gdouble]

@@ -1163,7 +1163,7 @@

poppler_structure_element_get_end_indent ()

-
gdouble
+
gdouble
 poppler_structure_element_get_end_indent
                                (PopplerStructureElement *poppler_structure_element);

Obtains the amount of indentation at the end of the block-level structure element.

@@ -1191,7 +1191,7 @@

poppler_structure_element_get_form_description ()

-
gchar *
+
gchar *
 poppler_structure_element_get_form_description
                                (PopplerStructureElement *poppler_structure_element);

Obtains the textual description of the form element. Note that the @@ -1200,7 +1200,7 @@

The returned string is allocated by the function. When it is -not needed anymore, be sure to call g_free() on it.

+not needed anymore, be sure to call g_free() on it.

Parameters

@@ -1218,7 +1218,7 @@

Returns

-

A string, or NULL if the attribute +

A string, or NULL if the attribute is not defined.

[transfer full]

@@ -1315,7 +1315,7 @@

poppler_structure_element_get_height ()

-
gdouble
+
gdouble
 poppler_structure_element_get_height (PopplerStructureElement *poppler_structure_element);

Obtains the height of the block-level structure element. Note that for elements which do not specify a height, it has to be calculated, and in this case -1 is returned.

@@ -1344,7 +1344,7 @@

poppler_structure_element_get_id ()

-
gchar *
+
gchar *
 poppler_structure_element_get_id (PopplerStructureElement *poppler_structure_element);

Obtains the identifier of an element.

@@ -1365,7 +1365,7 @@

Returns

The identifier of the element (if -defined), or NULL.

+defined), or NULL.

[transfer full]

Since: 0.26

@@ -1427,11 +1427,11 @@

poppler_structure_element_get_language ()

-
gchar *
+
gchar *
 poppler_structure_element_get_language
                                (PopplerStructureElement *poppler_structure_element);

Obtains the language and country code for the content in an element, -in two-letter ISO format, e.g. en_ES, or NULL if not +in two-letter ISO format, e.g. en_ES, or NULL if not defined.

Parameters

@@ -1450,7 +1450,7 @@

Returns

-

language and country code, or NULL.

+

language and country code, or NULL.

[transfer full]

Since: 0.26

@@ -1458,7 +1458,7 @@

poppler_structure_element_get_line_height ()

-
gdouble
+
gdouble
 poppler_structure_element_get_line_height
                                (PopplerStructureElement *poppler_structure_element);

Obtains the line height for the text contained in the inline-level structure element. @@ -1519,7 +1519,7 @@

poppler_structure_element_get_padding ()

void
 poppler_structure_element_get_padding (PopplerStructureElement *poppler_structure_element,
-                                       gdouble *paddings);
+ gdouble *paddings);

Obtains the padding of an element (space around it). The result values are in before-after-start-end ordering. For example using Western left-to-right writing, that is top-bottom-left-right.

@@ -1550,7 +1550,7 @@

poppler_structure_element_get_page ()

-
gint
+
gint
 poppler_structure_element_get_page (PopplerStructureElement *poppler_structure_element);

Obtains the page number in which the element is contained.

@@ -1664,7 +1664,7 @@

poppler_structure_element_get_space_after ()

-
gdouble
+
gdouble
 poppler_structure_element_get_space_after
                                (PopplerStructureElement *poppler_structure_element);

Obtains the amount of empty space after the block-level structure element.

@@ -1692,7 +1692,7 @@

poppler_structure_element_get_space_before ()

-
gdouble
+
gdouble
 poppler_structure_element_get_space_before
                                (PopplerStructureElement *poppler_structure_element);

Obtains the amount of empty space before the block-level structure element.

@@ -1720,7 +1720,7 @@

poppler_structure_element_get_start_indent ()

-
gdouble
+
gdouble
 poppler_structure_element_get_start_indent
                                (PopplerStructureElement *poppler_structure_element);

Obtains the amount of indentation at the beginning of the block-level structure element.

@@ -1782,7 +1782,7 @@

poppler_structure_element_get_table_column_span ()

-
guint
+
guint
 poppler_structure_element_get_table_column_span
                                (PopplerStructureElement *poppler_structure_element);

Obtains the number of columns the table element spans to.

@@ -1810,14 +1810,14 @@

poppler_structure_element_get_table_headers ()

-
gchar **
+
gchar **
 poppler_structure_element_get_table_headers
                                (PopplerStructureElement *poppler_structure_element);

Obtains an array with the names of the table column headers. This is only useful for table header row elements.

The array with the results is allocated by the function. The number -of items in the returned array can be obtained with g_strv_length(). -The returned value must be freed using g_strfreev().

+of items in the returned array can be obtained with g_strv_length(). +The returned value must be freed using g_strfreev().

Parameters

@@ -1836,7 +1836,7 @@

Returns

Zero-terminated array of strings with the table header names, -or NULL if the attribute is not defined.

+or NULL if the attribute is not defined.

[transfer full][array zero-terminated=1][element-type gchar*]

Since: 0.26

@@ -1847,7 +1847,7 @@

void poppler_structure_element_get_table_padding (PopplerStructureElement *poppler_structure_element, - gdouble *paddings); + gdouble *paddings);

Obtains the padding between the table cell’s content rectangle and the surrounding border of a block-level structure element. The result values are in before-after-start-end ordering (for the typical @@ -1879,7 +1879,7 @@

poppler_structure_element_get_table_row_span ()

-
guint
+
guint
 poppler_structure_element_get_table_row_span
                                (PopplerStructureElement *poppler_structure_element);

Obtains the number of rows the table element spans to.

@@ -1935,7 +1935,7 @@

poppler_structure_element_get_table_summary ()

-
gchar *
+
gchar *
 poppler_structure_element_get_table_summary
                                (PopplerStructureElement *poppler_structure_element);

Obtains the textual summary of the contents of the table element. Note that @@ -1945,7 +1945,7 @@

The returned string is allocated by the function. When it is -not needed anymore, be sure to call g_free() on it.

+not needed anymore, be sure to call g_free() on it.

Parameters

@@ -1963,7 +1963,7 @@

Returns

-

A string, or NULL if the attribute +

A string, or NULL if the attribute is not defined.

[transfer full]

@@ -1972,7 +1972,7 @@

poppler_structure_element_get_text ()

-
gchar *
+
gchar *
 poppler_structure_element_get_text (PopplerStructureElement *poppler_structure_element,
                                     PopplerStructureGetTextFlags flags);

Obtains the text enclosed by an element, or the text enclosed by the @@ -2039,7 +2039,7 @@

poppler_structure_element_get_text_decoration_color ()

-
gboolean
+
gboolean
 poppler_structure_element_get_text_decoration_color
                                (PopplerStructureElement *poppler_structure_element,
                                 PopplerColor *color);
@@ -2071,15 +2071,15 @@

Returns

-

TRUE if a color is defined for the element, -FALSE otherwise.

+

TRUE if a color is defined for the element, +FALSE otherwise.

Since: 0.26


poppler_structure_element_get_text_decoration_thickness ()

-
gdouble
+
gdouble
 poppler_structure_element_get_text_decoration_thickness
                                (PopplerStructureElement *poppler_structure_element);

Obtains the thickness of the text decoration for the text contained @@ -2139,7 +2139,7 @@

poppler_structure_element_get_text_indent ()

-
gdouble
+
gdouble
 poppler_structure_element_get_text_indent
                                (PopplerStructureElement *poppler_structure_element);

Obtains the amount of indentation of the text contained in the block-level structure element.

@@ -2170,7 +2170,7 @@

PopplerTextSpan ** poppler_structure_element_get_text_spans (PopplerStructureElement *poppler_structure_element, - guint *n_text_spans);

+ guint *n_text_spans);

Obtains the text enclosed by an element, as an array of PopplerTextSpan structures. Each item in the list is a piece of text which share the same attributes, plus its attributes. The following example shows how to @@ -2186,13 +2186,13 @@

-

+
guint i, n_spans;
-PopplerTextSpan **text_spans =
-   poppler_structure_element_get_text_spans (element, &n_spans);
-/* Use the text spans */
-for (i = 0; i < n_spans; i++)
-   poppler_text_span_free (text_spans[i]);
-g_free (text_spans);
guint i, n_spans;
+PopplerTextSpan **text_spans =
+   poppler_structure_element_get_text_spans (element, &n_spans);
+/* Use the text spans */
+for (i = 0; i < n_spans; i++)
+   poppler_text_span_free (text_spans[i]);
+g_free (text_spans);
@@ -2231,7 +2231,7 @@

poppler_structure_element_get_title ()

-
gchar *
+
gchar *
 poppler_structure_element_get_title (PopplerStructureElement *poppler_structure_element);

Obtains the title of an element.

@@ -2251,7 +2251,7 @@

Returns

-

The title of the element, or NULL.

+

The title of the element, or NULL.

[transfer full]

Since: 0.26

@@ -2259,7 +2259,7 @@

poppler_structure_element_get_width ()

-
gdouble
+
gdouble
 poppler_structure_element_get_width (PopplerStructureElement *poppler_structure_element);

Obtains the width of the block-level structure element. Note that for elements which do not specify a width, it has to be calculated, and in this case -1 is returned.

@@ -2317,7 +2317,7 @@

poppler_structure_element_is_block ()

-
gboolean
+
gboolean
 poppler_structure_element_is_block (PopplerStructureElement *poppler_structure_element);

Checks whether an element is a block element.

@@ -2337,14 +2337,14 @@

Returns

-

TRUE if the element is a block element, or FALSE otherwise.

+

TRUE if the element is a block element, or FALSE otherwise.

Since: 0.26


poppler_structure_element_is_content ()

-
gboolean
+
gboolean
 poppler_structure_element_is_content (PopplerStructureElement *poppler_structure_element);

Checks whether an element is actual document content.

@@ -2364,14 +2364,14 @@

Returns

-

TRUE if the element is content, or FALSE otherwise.

+

TRUE if the element is content, or FALSE otherwise.

Since: 0.26


poppler_structure_element_is_grouping ()

-
gboolean
+
gboolean
 poppler_structure_element_is_grouping (PopplerStructureElement *poppler_structure_element);

Checks whether an element is a grouping element.

@@ -2391,7 +2391,7 @@

Returns

-

TRUE if the element is a grouping element, FALSE +

TRUE if the element is a grouping element, FALSE otherwise.

Since: 0.26

@@ -2399,7 +2399,7 @@

poppler_structure_element_is_inline ()

-
gboolean
+
gboolean
 poppler_structure_element_is_inline (PopplerStructureElement *poppler_structure_element);

Checks whether an element is an inline element.

@@ -2419,7 +2419,7 @@

Returns

-

TRUE if the element is an inline element, or FALSE otherwise.

+

TRUE if the element is an inline element, or FALSE otherwise.

Since: 0.26

@@ -2544,7 +2544,7 @@

PopplerStructureElementIter * poppler_structure_element_iter_new (PopplerDocument *poppler_document);

Returns the root PopplerStructureElementIter for document -, or NULL. The +, or NULL. The returned value must be freed with poppler_structure_element_iter_free().

Documents may have an associated structure tree —mostly, Tagged-PDF compliant documents— which can be used to obtain information about @@ -2572,23 +2572,23 @@

-
static void
-walk_structure (PopplerStructureElementIter *iter)
-{
-  do {
-    /* Get the element and do something with it */
-    PopplerStructureElementIter *child = poppler_structure_element_iter_get_child (iter);
-    if (child)
-      walk_structure (child);
-    poppler_structure_element_iter_free (child);
-  } while (poppler_structure_element_iter_next (iter));
-}
-...
-{
-  iter = poppler_structure_element_iter_new (document);
-  walk_structure (iter);
-  poppler_structure_element_iter_free (iter);
-}
+
static void
+walk_structure (PopplerStructureElementIter *iter)
+{
+  do {
+    /* Get the element and do something with it */
+    PopplerStructureElementIter *child = poppler_structure_element_iter_get_child (iter);
+    if (child)
+      walk_structure (child);
+    poppler_structure_element_iter_free (child);
+  } while (poppler_structure_element_iter_next (iter));
+}
+...
+{
+  iter = poppler_structure_element_iter_new (document);
+  walk_structure (iter);
+  poppler_structure_element_iter_free (iter);
+}
@@ -2611,7 +2611,7 @@

Returns

-

a new PopplerStructureElementIter, or NULL if document +

a new PopplerStructureElementIter, or NULL if document doesn't have structure tree.

[transfer full]

@@ -2620,7 +2620,7 @@

poppler_structure_element_iter_next ()

-
gboolean
+
gboolean
 poppler_structure_element_iter_next (PopplerStructureElementIter *iter);

Sets iter to point to the next structure element at the current level @@ -2643,7 +2643,7 @@

Returns

-

TRUE, if iter +

TRUE, if iter was set to the next structure element

Since: 0.26

@@ -2756,6 +2756,7 @@

PopplerStructureElementIter

typedef struct _PopplerStructureElementIter PopplerStructureElementIter;
+

Since 24.10 this type supports g_autoptr


@@ -3520,6 +3521,6 @@

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/annotation-glossary.html b/glib/reference/html/annotation-glossary.html index c324b66..3079684 100644 --- a/glib/reference/html/annotation-glossary.html +++ b/glib/reference/html/annotation-glossary.html @@ -7,7 +7,7 @@ - + @@ -64,6 +64,6 @@

The data is owned by the callee, which is responsible of freeing it.

+
Generated by GTK-Doc V1.34.0
\ No newline at end of file diff --git a/glib/reference/html/api-index-0-12.html b/glib/reference/html/api-index-0-12.html index a3dbf06..5fe203c 100644 --- a/glib/reference/html/api-index-0-12.html +++ b/glib/reference/html/api-index-0-12.html @@ -8,7 +8,7 @@ - + @@ -117,6 +117,6 @@
+
Generated by GTK-Doc V1.34.0
\ No newline at end of file diff --git a/glib/reference/html/api-index-0-14.html b/glib/reference/html/api-index-0-14.html index 1cb5807..fab89ab 100644 --- a/glib/reference/html/api-index-0-14.html +++ b/glib/reference/html/api-index-0-14.html @@ -8,7 +8,7 @@ - + @@ -108,6 +108,6 @@

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/api-index-0-16.html b/glib/reference/html/api-index-0-16.html index 67aa473..1c354f5 100644 --- a/glib/reference/html/api-index-0-16.html +++ b/glib/reference/html/api-index-0-16.html @@ -8,7 +8,7 @@ - + @@ -168,12 +168,8 @@ poppler_page_render_for_printing_with_options, function in Poppler Page
-
-PopplerPrintFlags, enum in Poppler Page -
-
+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/api-index-0-18.html b/glib/reference/html/api-index-0-18.html index 19f0872..360577e 100644 --- a/glib/reference/html/api-index-0-18.html +++ b/glib/reference/html/api-index-0-18.html @@ -8,7 +8,7 @@ - + @@ -73,6 +73,6 @@
+
Generated by GTK-Doc V1.34.0
\ No newline at end of file diff --git a/glib/reference/html/api-index-0-20.html b/glib/reference/html/api-index-0-20.html index 473d52f..6a5b7cf 100644 --- a/glib/reference/html/api-index-0-20.html +++ b/glib/reference/html/api-index-0-20.html @@ -8,7 +8,7 @@ - + @@ -33,6 +33,6 @@

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/api-index-0-22.html b/glib/reference/html/api-index-0-22.html index 84ecad3..08ab46d 100644 --- a/glib/reference/html/api-index-0-22.html +++ b/glib/reference/html/api-index-0-22.html @@ -8,7 +8,7 @@ - + @@ -58,6 +58,6 @@

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-26.html b/glib/reference/html/api-index-0-26.html index 188a8c7..10173b2 100644 --- a/glib/reference/html/api-index-0-26.html +++ b/glib/reference/html/api-index-0-26.html @@ -8,7 +8,7 @@ - + @@ -417,6 +417,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-33.html b/glib/reference/html/api-index-0-33.html index 9f43a4a..13009f2 100644 --- a/glib/reference/html/api-index-0-33.html +++ b/glib/reference/html/api-index-0-33.html @@ -8,7 +8,7 @@ - + @@ -29,6 +29,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-46.html b/glib/reference/html/api-index-0-46.html index a458aa8..bf1d723 100644 --- a/glib/reference/html/api-index-0-46.html +++ b/glib/reference/html/api-index-0-46.html @@ -8,7 +8,7 @@ - + @@ -57,6 +57,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-54.html b/glib/reference/html/api-index-0-54.html index 5fd402f..6ff0984 100644 --- a/glib/reference/html/api-index-0-54.html +++ b/glib/reference/html/api-index-0-54.html @@ -8,7 +8,7 @@ - + @@ -33,6 +33,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-70.html b/glib/reference/html/api-index-0-70.html index 6f70714..d070b5f 100644 --- a/glib/reference/html/api-index-0-70.html +++ b/glib/reference/html/api-index-0-70.html @@ -8,7 +8,7 @@ - + @@ -56,6 +56,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-72.html b/glib/reference/html/api-index-0-72.html index 5050605..0e33e81 100644 --- a/glib/reference/html/api-index-0-72.html +++ b/glib/reference/html/api-index-0-72.html @@ -8,7 +8,7 @@ - + @@ -36,6 +36,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-73.html b/glib/reference/html/api-index-0-73.html index a7d0b52..8712083 100644 --- a/glib/reference/html/api-index-0-73.html +++ b/glib/reference/html/api-index-0-73.html @@ -8,7 +8,7 @@ - + @@ -51,6 +51,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-78.html b/glib/reference/html/api-index-0-78.html index 875a9fd..d72d029 100644 --- a/glib/reference/html/api-index-0-78.html +++ b/glib/reference/html/api-index-0-78.html @@ -8,7 +8,7 @@ - + @@ -29,6 +29,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-80.html b/glib/reference/html/api-index-0-80.html index af35333..48a2071 100644 --- a/glib/reference/html/api-index-0-80.html +++ b/glib/reference/html/api-index-0-80.html @@ -8,7 +8,7 @@ - + @@ -83,6 +83,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-82.html b/glib/reference/html/api-index-0-82.html index fc71848..5118e3a 100644 --- a/glib/reference/html/api-index-0-82.html +++ b/glib/reference/html/api-index-0-82.html @@ -8,7 +8,7 @@ - + @@ -29,6 +29,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-88.html b/glib/reference/html/api-index-0-88.html index af5a97a..52a2eee 100644 --- a/glib/reference/html/api-index-0-88.html +++ b/glib/reference/html/api-index-0-88.html @@ -8,7 +8,7 @@ - + @@ -29,6 +29,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-89.html b/glib/reference/html/api-index-0-89.html index b2d83ae..5d57009 100644 --- a/glib/reference/html/api-index-0-89.html +++ b/glib/reference/html/api-index-0-89.html @@ -8,7 +8,7 @@ - + @@ -29,6 +29,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-0-90.html b/glib/reference/html/api-index-0-90.html index f448ea9..ce8c2cc 100644 --- a/glib/reference/html/api-index-0-90.html +++ b/glib/reference/html/api-index-0-90.html @@ -8,7 +8,7 @@ - + @@ -40,6 +40,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-20-04-0.html b/glib/reference/html/api-index-20-04-0.html index e6e1ed9..69eefe6 100644 --- a/glib/reference/html/api-index-20-04-0.html +++ b/glib/reference/html/api-index-20-04-0.html @@ -8,7 +8,7 @@ - + @@ -37,6 +37,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-20-09-0.html b/glib/reference/html/api-index-20-09-0.html index 16359f8..47cfa43 100644 --- a/glib/reference/html/api-index-20-09-0.html +++ b/glib/reference/html/api-index-20-09-0.html @@ -8,7 +8,7 @@ - + @@ -76,6 +76,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-21-05-0.html b/glib/reference/html/api-index-21-05-0.html index ae32df4..f1876f4 100644 --- a/glib/reference/html/api-index-21-05-0.html +++ b/glib/reference/html/api-index-21-05-0.html @@ -8,7 +8,7 @@ - + @@ -33,6 +33,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-21-12-0.html b/glib/reference/html/api-index-21-12-0.html index 25ec507..b0825c6 100644 --- a/glib/reference/html/api-index-21-12-0.html +++ b/glib/reference/html/api-index-21-12-0.html @@ -8,7 +8,7 @@ - + @@ -119,6 +119,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/api-index-full.html b/glib/reference/html/api-index-full.html index eb4ea43..2247886 100644 --- a/glib/reference/html/api-index-full.html +++ b/glib/reference/html/api-index-full.html @@ -8,7 +8,7 @@ - + @@ -254,14 +254,38 @@
+poppler_annot_free_text_get_font_color, function in Poppler Annotation +
+
+
+poppler_annot_free_text_get_font_desc, function in Poppler Annotation +
+
+
poppler_annot_free_text_get_quadding, function in Poppler Annotation
+poppler_annot_free_text_new, function in Poppler Annotation +
+
+
+poppler_annot_free_text_set_font_color, function in Poppler Annotation +
+
+
+poppler_annot_free_text_set_font_desc, function in Poppler Annotation +
+
+
poppler_annot_get_annot_type, function in Poppler Annotation
+poppler_annot_get_border_width, function in Poppler Annotation +
+
+
poppler_annot_get_color, function in Poppler Annotation
@@ -378,6 +402,10 @@
+poppler_annot_set_border_width, function in Poppler Annotation +
+
+
poppler_annot_set_color, function in Poppler Annotation
@@ -556,10 +584,58 @@

C

+PopplerCertificateInfo, struct in PopplerFormField +
+
+
PopplerCertificateStatus, enum in PopplerFormField
+poppler_certificate_info_copy, function in PopplerFormField +
+
+
+poppler_certificate_info_free, function in PopplerFormField +
+
+
+poppler_certificate_info_get_expiration_time, function in PopplerFormField +
+
+
+poppler_certificate_info_get_issuance_time, function in PopplerFormField +
+
+
+poppler_certificate_info_get_issuer_common_name, function in PopplerFormField +
+
+
+poppler_certificate_info_get_issuer_email, function in PopplerFormField +
+
+
+poppler_certificate_info_get_issuer_organization, function in PopplerFormField +
+
+
+poppler_certificate_info_get_subject_common_name, function in PopplerFormField +
+
+
+poppler_certificate_info_get_subject_email, function in PopplerFormField +
+
+
+poppler_certificate_info_get_subject_organization, function in PopplerFormField +
+
+
+poppler_certificate_info_new, function in PopplerFormField +
+
+
POPPLER_CHECK_VERSION, macro in Poppler Features
@@ -955,6 +1031,10 @@
+PopplerFontDescription, struct in Poppler Annotation +
+
+
PopplerFontInfo, struct in PopplerDocument
@@ -1011,6 +1091,18 @@
+poppler_font_description_copy, function in Poppler Annotation +
+
+
+poppler_font_description_free, function in Poppler Annotation +
+
+
+poppler_font_description_new, function in Poppler Annotation +
+
+
poppler_font_info_free, function in PopplerDocument
@@ -1208,10 +1300,18 @@

G

+poppler_get_available_signing_certificates, function in PopplerFormField +
+
+
poppler_get_backend, function in Poppler Features
+poppler_get_nss_dir, function in PopplerFormField +
+
+
poppler_get_version, function in Poppler Features
@@ -1642,6 +1742,10 @@
+poppler_page_render_full, function in Poppler Page +
+
+
poppler_page_render_selection, function in Poppler Page
@@ -1702,10 +1806,6 @@
-PopplerPrintFlags, enum in Poppler Page -
-
-
PopplerPrintScaling, enum in PopplerDocument
@@ -1781,6 +1881,14 @@
+poppler_set_nss_dir, function in PopplerFormField +
+
+
+poppler_set_nss_password_callback, function in PopplerFormField +
+
+
PopplerSignatureInfo, struct in PopplerFormField
@@ -1801,6 +1909,10 @@
+poppler_signature_info_get_certificate_info, function in PopplerFormField +
+
+
poppler_signature_info_get_certificate_status, function in PopplerFormField
@@ -1817,6 +1929,170 @@
+poppler_signing_data_copy, function in PopplerFormField +
+
+
+poppler_signing_data_free, function in PopplerFormField +
+
+
+poppler_signing_data_get_background_color, function in PopplerFormField +
+
+
+poppler_signing_data_get_border_color, function in PopplerFormField +
+
+
+poppler_signing_data_get_border_width, function in PopplerFormField +
+
+
+poppler_signing_data_get_certificate_info, function in PopplerFormField +
+
+
+poppler_signing_data_get_destination_filename, function in PopplerFormField +
+
+
+poppler_signing_data_get_document_owner_password, function in PopplerFormField +
+
+
+poppler_signing_data_get_document_user_password, function in PopplerFormField +
+
+
+poppler_signing_data_get_field_partial_name, function in PopplerFormField +
+
+
+poppler_signing_data_get_font_color, function in PopplerFormField +
+
+
+poppler_signing_data_get_font_size, function in PopplerFormField +
+
+
+poppler_signing_data_get_image_path, function in PopplerFormField +
+
+
+poppler_signing_data_get_left_font_size, function in PopplerFormField +
+
+
+poppler_signing_data_get_location, function in PopplerFormField +
+
+
+poppler_signing_data_get_page, function in PopplerFormField +
+
+
+poppler_signing_data_get_password, function in PopplerFormField +
+
+
+poppler_signing_data_get_reason, function in PopplerFormField +
+
+
+poppler_signing_data_get_signature_rectangle, function in PopplerFormField +
+
+
+poppler_signing_data_get_signature_text, function in PopplerFormField +
+
+
+poppler_signing_data_get_signature_text_left, function in PopplerFormField +
+
+
+poppler_signing_data_new, function in PopplerFormField +
+
+
+poppler_signing_data_set_background_color, function in PopplerFormField +
+
+
+poppler_signing_data_set_border_color, function in PopplerFormField +
+
+
+poppler_signing_data_set_border_width, function in PopplerFormField +
+
+
+poppler_signing_data_set_certificate_info, function in PopplerFormField +
+
+
+poppler_signing_data_set_destination_filename, function in PopplerFormField +
+
+
+poppler_signing_data_set_document_owner_password, function in PopplerFormField +
+
+
+poppler_signing_data_set_document_user_password, function in PopplerFormField +
+
+
+poppler_signing_data_set_field_partial_name, function in PopplerFormField +
+
+
+poppler_signing_data_set_font_color, function in PopplerFormField +
+
+
+poppler_signing_data_set_font_size, function in PopplerFormField +
+
+
+poppler_signing_data_set_image_path, function in PopplerFormField +
+
+
+poppler_signing_data_set_left_font_size, function in PopplerFormField +
+
+
+poppler_signing_data_set_location, function in PopplerFormField +
+
+
+poppler_signing_data_set_page, function in PopplerFormField +
+
+
+poppler_signing_data_set_password, function in PopplerFormField +
+
+
+poppler_signing_data_set_reason, function in PopplerFormField +
+
+
+poppler_signing_data_set_signature_rectangle, function in PopplerFormField +
+
+
+poppler_signing_data_set_signature_text, function in PopplerFormField +
+
+
+poppler_signing_data_set_signature_text_left, function in PopplerFormField +
+
+
PopplerStructureBlockAlign, enum in PopplerStructureElement
@@ -2192,6 +2468,6 @@
+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/ch01.html b/glib/reference/html/ch01.html index 5f4b7e8..26ae3ff 100644 --- a/glib/reference/html/ch01.html +++ b/glib/reference/html/ch01.html @@ -8,7 +8,7 @@ - + @@ -71,6 +71,6 @@ +
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/index.html b/glib/reference/html/index.html index 3584b52..24396f9 100644 --- a/glib/reference/html/index.html +++ b/glib/reference/html/index.html @@ -6,7 +6,7 @@ - + @@ -15,7 +15,7 @@

- for Poppler 22.12.0 + for Poppler 25.03.0


@@ -97,6 +97,6 @@ +
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Error-handling.html b/glib/reference/html/poppler-Error-handling.html index 0993cb2..04a0f7b 100644 --- a/glib/reference/html/poppler-Error-handling.html +++ b/glib/reference/html/poppler-Error-handling.html @@ -8,7 +8,7 @@ - + @@ -76,7 +76,7 @@

#define POPPLER_ERROR poppler_error_quark()

Error domain for poppler operations. Errors in this domain will -be from the PopplerError enumeration. See GError for information +be from the PopplerError enumeration. See GError for information on error domains.


@@ -127,6 +127,11 @@

  + +

POPPLER_ERROR_SIGNING

+  +  + @@ -134,6 +139,6 @@

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-PDF-Utility-functions.html b/glib/reference/html/poppler-PDF-Utility-functions.html index fbed426..450b102 100644 --- a/glib/reference/html/poppler-PDF-Utility-functions.html +++ b/glib/reference/html/poppler-PDF-Utility-functions.html @@ -8,7 +8,7 @@ - + @@ -41,7 +41,7 @@

-gboolean +gboolean poppler_date_parse () @@ -57,7 +57,7 @@

-guint8 * +guint8 * poppler_named_dest_to_bytestring () @@ -78,10 +78,10 @@

Functions

poppler_date_parse ()

-
gboolean
-poppler_date_parse (const gchar *date,
+
gboolean
+poppler_date_parse (const gchar *date,
                     time_t *timet);
-

Parses a PDF format date string and converts it to a time_t. Returns FALSE +

Parses a PDF format date string and converts it to a time_t. Returns FALSE if the parsing fails or the input string is not a valid PDF format date string

Parameters

@@ -107,7 +107,7 @@

Returns

-

TRUE, if timet +

TRUE, if timet was set

Since: 0.12

@@ -116,13 +116,13 @@

poppler_named_dest_from_bytestring ()

char *
-poppler_named_dest_from_bytestring (const guint8 *data,
-                                    gsize length);
+poppler_named_dest_from_bytestring (const guint8 *data, + gsize length);

Converts a bytestring into a zero-terminated string suitable to pass to poppler_document_find_dest().

Note that the returned string has no defined encoding and is not suitable for display to the user.

-

The returned data must be freed using g_free().

+

The returned data must be freed using g_free().

Parameters

@@ -155,17 +155,17 @@

poppler_named_dest_to_bytestring ()

-
guint8 *
+
guint8 *
 poppler_named_dest_to_bytestring (const char *name,
-                                  gsize *length);
+ gsize *length);

Converts a named dest string (e.g. from PopplerDest.named_dest) into a bytestring, inverting the transformation of poppler_named_dest_from_bytestring().

Note that the returned data is not zero terminated and may also contains embedded NUL bytes.

If name - is not a valid named dest string, returns NULL.

-

The returned data must be freed using g_free().

+ is not a valid named dest string, returns NULL.

+

The returned data must be freed using g_free().

Parameters

@@ -191,7 +191,7 @@

Returns

a new bytestring, -or NULL.

+or NULL.

[array length=length][transfer full][nullable]

Since: 0.73

@@ -199,6 +199,6 @@

-
Generated by GTK-Doc V1.33.1 +
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Annotation.html b/glib/reference/html/poppler-Poppler-Annotation.html index 57e67da..cd34c6b 100644 --- a/glib/reference/html/poppler-Poppler-Annotation.html +++ b/glib/reference/html/poppler-Poppler-Annotation.html @@ -8,7 +8,7 @@ - + @@ -106,7 +106,7 @@

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-gchar * +gchar * poppler_annot_file_attachment_get_name () @@ -122,6 +122,22 @@

+PopplerColor * + +poppler_annot_free_text_get_font_color () +
+PopplerFontDescription * + +poppler_annot_free_text_get_font_desc () +
PopplerAnnotFreeTextQuadding @@ -130,6 +146,30 @@

+PopplerAnnot * + +poppler_annot_free_text_new () +
+void + +poppler_annot_free_text_set_font_desc () +
+void + +poppler_annot_free_text_set_font_color () +
PopplerColor * @@ -138,7 +178,7 @@

-gchar * +gchar * poppler_annot_get_contents () @@ -154,7 +194,7 @@

-gchar * +gchar * poppler_annot_get_modified () @@ -162,7 +202,7 @@

-gchar * +gchar * poppler_annot_get_name () @@ -170,7 +210,7 @@

-gint +gint poppler_annot_get_page_index () @@ -186,6 +226,14 @@

+gboolean + +poppler_annot_get_border_width () +
PopplerAnnot * @@ -202,7 +250,7 @@

-GDate * +GDate * poppler_annot_markup_get_date () @@ -218,7 +266,7 @@

-gchar * +gchar * poppler_annot_markup_get_label () @@ -226,7 +274,7 @@

-gdouble +gdouble poppler_annot_markup_get_opacity () @@ -234,7 +282,7 @@

-gboolean +gboolean poppler_annot_markup_get_popup_is_open () @@ -242,7 +290,7 @@

-gboolean +gboolean poppler_annot_markup_get_popup_rectangle () @@ -258,7 +306,7 @@

-gchar * +gchar * poppler_annot_markup_get_subject () @@ -266,7 +314,7 @@

-gboolean +gboolean poppler_annot_markup_has_popup () @@ -322,7 +370,7 @@

-gchar * +gchar * poppler_annot_movie_get_title () @@ -370,6 +418,14 @@

+void + +poppler_annot_set_border_width () +
PopplerColor * @@ -410,7 +466,7 @@

-gboolean +gboolean poppler_annot_stamp_set_custom_image () @@ -426,7 +482,7 @@

-gchar * +gchar * poppler_annot_text_get_icon () @@ -434,7 +490,7 @@

-gboolean +gboolean poppler_annot_text_get_is_open () @@ -450,7 +506,7 @@

-GArray * +GArray * poppler_annot_text_markup_get_quadrilaterals () @@ -520,10 +576,34 @@

poppler_annot_text_set_is_open ()

+PopplerFontDescription * + +poppler_font_description_copy () +
+void + +poppler_font_description_free () +
+PopplerFontDescription * + +poppler_font_description_new () +
-
+

Types and Values

@@ -612,6 +692,10 @@

PopplerAnnotType

+ + + + @@ -653,7 +737,8 @@

Object Hierarchy

    GBoxed
-    ╰── PopplerAnnotCalloutLine
+    ├── PopplerAnnotCalloutLine
+    ╰── PopplerFontDescription
     GEnum
     ├── PopplerAnnotExternalDataType
     ├── PopplerAnnotFreeTextQuadding
@@ -663,7 +748,7 @@ 

╰── PopplerAnnotType GFlags ╰── PopplerAnnotFlag - GObject + GObject ╰── PopplerAnnot ├── PopplerAnnotMarkup │ ├── PopplerAnnotCircle @@ -671,6 +756,7 @@

│ ├── PopplerAnnotFreeText │ ├── PopplerAnnotLine │ ├── PopplerAnnotSquare + │ ├── PopplerAnnotStamp │ ├── PopplerAnnotText │ ├── PopplerAnnotTextMarkup │ ├── PopplerAnnotCircle @@ -678,8 +764,7 @@

│ ├── PopplerAnnotFreeText │ ╰── PopplerAnnotLine ├── PopplerAnnotMovie - ├── PopplerAnnotScreen - ╰── PopplerAnnotStamp + ╰── PopplerAnnotScreen

@@ -745,7 +830,7 @@

Returns

a new allocated PopplerAnnotCalloutLine as exact copy of callout -, NULL in other case. It must be freed when done.

+, NULL in other case. It must be freed when done.


@@ -778,7 +863,7 @@

Creates a new empty PopplerAnnotCalloutLine.

Returns

-

a new allocated PopplerAnnotCalloutLine, NULL in other case. +

a new allocated PopplerAnnotCalloutLine, NULL in other case. It must be freed when done.

@@ -809,7 +894,7 @@

Returns

a new allocated PopplerColor with the color values of poppler_annot -, or NULL. It must be freed with g_free() when done.

+, or NULL. It must be freed with g_free() when done.

Since: 0.26

@@ -876,7 +961,7 @@

- + @@ -919,7 +1004,7 @@

poppler_annot_file_attachment_get_name ()

-
gchar *
+
gchar *
 poppler_annot_file_attachment_get_name
                                (PopplerAnnotFileAttachment *poppler_annot);

Retrieves the name of poppler_annot @@ -943,7 +1028,7 @@

Returns

a new allocated string with the name of poppler_annot . It must -be freed with g_free() when done.

+be freed with g_free() when done.

Since: 0.14

@@ -974,12 +1059,70 @@

Returns

a new allocated PopplerAnnotCalloutLine if the annot has a callout -line, NULL in other case. It must be freed with g_free() when +line, NULL in other case. It must be freed with g_free() when done.


+

poppler_annot_free_text_get_font_color ()

+
PopplerColor *
+poppler_annot_free_text_get_font_color
+                               (PopplerAnnotFreeText *poppler_annot);
+

Gets the font color.

+
+

Parameters

+

 PopplerFontDescription
#define POPPLER_ANNOT_TEXT_ICON_CIRCLE

poppler_color

a PopplerColor, or NULL.

a PopplerColor, or NULL.

[allow-none]
+++++ + + + + + +

poppler_annot

a PopplerAnnotFreeText

 
+
+
+

Returns

+

a copy of the font's PopplerColor.

+

[transfer full]

+
+

Since: 24.12.0

+
+
+
+

poppler_annot_free_text_get_font_desc ()

+
PopplerFontDescription *
+poppler_annot_free_text_get_font_desc (PopplerAnnotFreeText *poppler_annot);
+

Gets the font description (i.e. font family name, style, weight, stretch and size).

+
+

Parameters

+
+++++ + + + + + +

poppler_annot

a PopplerAnnotFreeText

 
+
+
+

Returns

+

a copy of the annotation font description, or NULL if there is +no font description set.

+

[nullable][transfer full]

+
+

Since: 24.12.0

+
+
+

poppler_annot_free_text_get_quadding ()

PopplerAnnotFreeTextQuadding
 poppler_annot_free_text_get_quadding (PopplerAnnotFreeText *poppler_annot);
@@ -1008,6 +1151,110 @@


+

poppler_annot_free_text_new ()

+
PopplerAnnot *
+poppler_annot_free_text_new (PopplerDocument *doc,
+                             PopplerRectangle *rect);
+

Creates a new Free Text annotation that will be +located on rect + when added to a page. See +poppler_page_add_annot(). It initially has no content. Font family, size and +color are initially undefined and must be set, see +poppler_annot_free_text_set_font_desc() and +poppler_annot_free_text_set_font_color().

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

doc

a PopplerDocument

 

rect

a PopplerRectangle

 
+
+
+

Returns

+

A newly created PopplerAnnotFreeText annotation.

+

[transfer full]

+
+
+
+
+

poppler_annot_free_text_set_font_desc ()

+
void
+poppler_annot_free_text_set_font_desc (PopplerAnnotFreeText *poppler_annot,
+                                       PopplerFontDescription *font_desc);
+

Sets the font description (i.e. font family name, style, weight, stretch and size).

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

poppler_annot

a PopplerAnnotFreeText

 

font_desc

a PopplerFontDescription

 
+
+

Since: 24.12.0

+
+
+
+

poppler_annot_free_text_set_font_color ()

+
void
+poppler_annot_free_text_set_font_color
+                               (PopplerAnnotFreeText *poppler_annot,
+                                PopplerColor *color);
+

Sets the font color.

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

poppler_annot

a PopplerAnnotFreeText

 

color

a PopplerColor

 
+
+

Since: 24.12.0

+
+
+

poppler_annot_get_color ()

PopplerColor *
 poppler_annot_get_color (PopplerAnnot *poppler_annot);
@@ -1032,13 +1279,13 @@

Returns

a new allocated PopplerColor with the color values of poppler_annot -, or NULL. It must be freed with g_free() when done.

+, or NULL. It must be freed with g_free() when done.


poppler_annot_get_contents ()

-
gchar *
+
gchar *
 poppler_annot_get_contents (PopplerAnnot *poppler_annot);

Retrieves the contents of poppler_annot .

@@ -1061,7 +1308,7 @@

Returns

a new allocated string with the contents of poppler_annot . It -must be freed with g_free() when done.

+must be freed with g_free() when done.


@@ -1096,7 +1343,7 @@

poppler_annot_get_modified ()

-
gchar *
+
gchar *
 poppler_annot_get_modified (PopplerAnnot *poppler_annot);

Retrieves the last modification data of poppler_annot . The returned @@ -1121,13 +1368,13 @@

Returns

a new allocated string with the last modification data of poppler_annot -. It must be freed with g_free() when done.

+. It must be freed with g_free() when done.


poppler_annot_get_name ()

-
gchar *
+
gchar *
 poppler_annot_get_name (PopplerAnnot *poppler_annot);

Retrieves the name of poppler_annot .

@@ -1150,13 +1397,13 @@

Returns

a new allocated string with the name of poppler_annot . It must -be freed with g_free() when done.

+be freed with g_free() when done.


poppler_annot_get_page_index ()

-
gint
+
gint
 poppler_annot_get_page_index (PopplerAnnot *poppler_annot);

Returns the page index to which poppler_annot is associated, or -1 if unknown

@@ -1216,6 +1463,45 @@


+

poppler_annot_get_border_width ()

+
gboolean
+poppler_annot_get_border_width (PopplerAnnot *poppler_annot,
+                                double *width);
+

Returns the border width of the annotation. Some PDF editors set a border +width even if the border is not actually drawn.

+
+

Parameters

+
+++++ + + + + + + + + + + + + +

poppler_annot

a PopplerAnnot

 

width

a valid pointer to a double

 
+
+
+

Returns

+

true and sets border_width +to the actual border width if a border +is defined, otherwise returns false and sets border_width +to 0.

+
+

Since: 24.12.0

+
+
+

poppler_annot_line_new ()

PopplerAnnot *
 poppler_annot_line_new (PopplerDocument *doc,
@@ -1305,7 +1591,7 @@ 

poppler_annot_markup_get_date ()

-
GDate *
+
GDate *
 poppler_annot_markup_get_date (PopplerAnnotMarkup *poppler_annot);

Returns the date and time when the annotation was created

@@ -1325,8 +1611,8 @@

Returns

-

a GDate representing the date and time -when the annotation was created, or NULL.

+

a GDate representing the date and time +when the annotation was created, or NULL.

[transfer full]

@@ -1362,7 +1648,7 @@

poppler_annot_markup_get_label ()

-
gchar *
+
gchar *
 poppler_annot_markup_get_label (PopplerAnnotMarkup *poppler_annot);

Retrieves the label text of poppler_annot .

@@ -1390,7 +1676,7 @@

poppler_annot_markup_get_opacity ()

-
gdouble
+
gdouble
 poppler_annot_markup_get_opacity (PopplerAnnotMarkup *poppler_annot);

Retrieves the opacity value of poppler_annot .

@@ -1419,7 +1705,7 @@

poppler_annot_markup_get_popup_is_open ()

-
gboolean
+
gboolean
 poppler_annot_markup_get_popup_is_open
                                (PopplerAnnotMarkup *poppler_annot);

Retrieves the state of the popup window related to poppler_annot @@ -1442,14 +1728,14 @@

Returns

the state of poppler_annot -. TRUE if it's open, FALSE in +. TRUE if it's open, FALSE in other case.


poppler_annot_markup_get_popup_rectangle ()

-
gboolean
+
gboolean
 poppler_annot_markup_get_popup_rectangle
                                (PopplerAnnotMarkup *poppler_annot,
                                 PopplerRectangle *poppler_rect);
@@ -1479,7 +1765,7 @@

Returns

-

TRUE if PopplerRectangle was correctly filled, FALSE otherwise

+

TRUE if PopplerRectangle was correctly filled, FALSE otherwise

Since: 0.12

@@ -1514,7 +1800,7 @@

poppler_annot_markup_get_subject ()

-
gchar *
+
gchar *
 poppler_annot_markup_get_subject (PopplerAnnotMarkup *poppler_annot);

Retrives the subject text of poppler_annot .

@@ -1542,9 +1828,9 @@

poppler_annot_markup_has_popup ()

-
gboolean
+
gboolean
 poppler_annot_markup_has_popup (PopplerAnnotMarkup *poppler_annot);
-

Return TRUE if the markup annotation has a popup window associated

+

Return TRUE if the markup annotation has a popup window associated

Parameters

@@ -1562,8 +1848,8 @@

Returns

-

TRUE, if poppler_annot -has popup, FALSE otherwise

+

TRUE, if poppler_annot +has popup, FALSE otherwise

Since: 0.12

@@ -1572,7 +1858,7 @@

poppler_annot_markup_set_label ()

void
 poppler_annot_markup_set_label (PopplerAnnotMarkup *poppler_annot,
-                                const gchar *label);
+ const gchar *label);

Sets the label text of poppler_annot , replacing the current one

@@ -1591,7 +1877,7 @@

- + @@ -1604,7 +1890,7 @@

poppler_annot_markup_set_opacity ()

void
 poppler_annot_markup_set_opacity (PopplerAnnotMarkup *poppler_annot,
-                                  gdouble opacity);
+ gdouble opacity);

Sets the opacity of poppler_annot . This value applies to all visible elements of poppler_annot @@ -1674,7 +1960,7 @@

void poppler_annot_markup_set_popup_is_open (PopplerAnnotMarkup *poppler_annot, - gboolean is_open); + gboolean is_open);

Sets the state of the popup window related to poppler_annot .

@@ -1772,7 +2058,7 @@

poppler_annot_movie_get_title ()

-
gchar *
+
gchar *
 poppler_annot_movie_get_title (PopplerAnnotMovie *poppler_annot);

Retrieves the movie title of poppler_annot .

@@ -1853,7 +2139,7 @@

- + @@ -1866,7 +2152,7 @@

poppler_annot_set_contents ()

void
 poppler_annot_set_contents (PopplerAnnot *poppler_annot,
-                            const gchar *contents);
+ const gchar *contents);

Sets the contents of poppler_annot to the given value, replacing the current contents.

@@ -1962,6 +2248,40 @@


+

poppler_annot_set_border_width ()

+
void
+poppler_annot_set_border_width (PopplerAnnot *poppler_annot,
+                                double width);
+

Sets the border width of the annotation. Since there is currently no +mechanism in the GLib binding to control the appearance of the border width, +this should generally only be used to disable the border, although the +API might be completed in the future.

+
+

Parameters

+

label

a text string containing the new label, or NULL.

a text string containing the new label, or NULL.

[allow-none]

poppler_color

a PopplerColor, or NULL.

a PopplerColor, or NULL.

[allow-none]
+++++ + + + + + + + + + + + + +

poppler_annot

a PopplerAnnot

 

width

the new border width

 
+
+

Since: 24.12.0

+
+
+

poppler_annot_square_get_interior_color ()

PopplerColor *
 poppler_annot_square_get_interior_color
@@ -1987,7 +2307,7 @@ 

Returns

a new allocated PopplerColor with the color values of poppler_annot -, or NULL. It must be freed with g_free() when done.

+, or NULL. It must be freed with g_free() when done.

Since: 0.26

@@ -2054,7 +2374,7 @@

poppler_color

-

a PopplerColor, or NULL.

+

a PopplerColor, or NULL.

[allow-none] @@ -2129,10 +2449,10 @@

poppler_annot_stamp_set_custom_image ()

-
gboolean
+
gboolean
 poppler_annot_stamp_set_custom_image (PopplerAnnotStamp *poppler_annot,
-                                      cairo_surface_t *image,
-                                      GError **error);
+ cairo_surface_t *image, + GError **error);

Sets the custom image of poppler_annot to be image

@@ -2157,7 +2477,7 @@

error

-

return location for error, or NULL.

+

return location for error, or NULL.

[nullable] @@ -2165,7 +2485,7 @@

Returns

-

TRUE on success, FALSE otherwise.

+

TRUE on success, FALSE otherwise.

Since: 22.07.0

@@ -2204,7 +2524,7 @@

poppler_annot_text_get_icon ()

-
gchar *
+
gchar *
 poppler_annot_text_get_icon (PopplerAnnotText *poppler_annot);

Gets name of the icon of poppler_annot .

@@ -2231,7 +2551,7 @@

poppler_annot_text_get_is_open ()

-
gboolean
+
gboolean
 poppler_annot_text_get_is_open (PopplerAnnotText *poppler_annot);

Retrieves the state of poppler_annot .

@@ -2253,7 +2573,7 @@

Returns

the state of poppler_annot -. TRUE if it's open, FALSE in +. TRUE if it's open, FALSE in other case.

@@ -2288,10 +2608,10 @@

poppler_annot_text_markup_get_quadrilaterals ()

-
GArray *
+
GArray *
 poppler_annot_text_markup_get_quadrilaterals
                                (PopplerAnnotTextMarkup *poppler_annot);
-

Returns a GArray of PopplerQuadrilateral items that map from a +

Returns a GArray of PopplerQuadrilateral items that map from a location on page to a PopplerAnnotTextMarkup. This array must be freed when done.

@@ -2312,7 +2632,7 @@

Returns

-

A GArray of PopplerQuadrilateral.

+

A GArray of PopplerQuadrilateral.

[element-type PopplerQuadrilateral][transfer full]

Since: 0.26

@@ -2324,7 +2644,7 @@

PopplerDocument *doc, PopplerRectangle *rect, - GArray *quadrilaterals);

+ GArray *quadrilaterals);

Creates a new Highlight Text annotation that will be located on rect when added to a page. See poppler_page_add_annot()

@@ -2349,7 +2669,7 @@

quadrilaterals

-

A GArray of +

A GArray of PopplerQuadrilaterals.

[element-type PopplerQuadrilateral] @@ -2370,7 +2690,7 @@

PopplerDocument *doc, PopplerRectangle *rect, - GArray *quadrilaterals);

+ GArray *quadrilaterals);

Creates a new Squiggly Text annotation that will be located on rect when added to a page. See poppler_page_add_annot()

@@ -2395,7 +2715,7 @@

quadrilaterals

-

A GArray of +

A GArray of PopplerQuadrilaterals.

[element-type PopplerQuadrilateral] @@ -2416,7 +2736,7 @@

PopplerDocument *doc, PopplerRectangle *rect, - GArray *quadrilaterals);

+ GArray *quadrilaterals);

Creates a new Strike Out Text annotation that will be located on rect when added to a page. See poppler_page_add_annot()

@@ -2441,7 +2761,7 @@

quadrilaterals

-

A GArray of +

A GArray of PopplerQuadrilaterals.

[element-type PopplerQuadrilateral] @@ -2462,7 +2782,7 @@

PopplerDocument *doc, PopplerRectangle *rect, - GArray *quadrilaterals); + GArray *quadrilaterals);

Creates a new Underline Text annotation that will be located on rect when added to a page. See poppler_page_add_annot()

@@ -2487,7 +2807,7 @@

quadrilaterals

-

A GArray of +

A GArray of PopplerQuadrilaterals.

[element-type PopplerQuadrilateral] @@ -2507,7 +2827,7 @@

void poppler_annot_text_markup_set_quadrilaterals (PopplerAnnotTextMarkup *poppler_annot, - GArray *quadrilaterals); + GArray *quadrilaterals);

Set the regions (Quadrilaterals) to apply the text markup in poppler_annot .

@@ -2526,7 +2846,7 @@

quadrilaterals

-

A GArray of +

A GArray of PopplerQuadrilaterals.

[element-type PopplerQuadrilateral] @@ -2578,7 +2898,7 @@

poppler_annot_text_set_icon ()

void
 poppler_annot_text_set_icon (PopplerAnnotText *poppler_annot,
-                             const gchar *icon);
+ const gchar *icon);

Sets the icon of poppler_annot . The following predefined icons are currently supported:

@@ -2655,7 +2975,7 @@

poppler_annot_text_set_is_open ()

void
 poppler_annot_text_set_is_open (PopplerAnnotText *poppler_annot,
-                                gboolean is_open);
+ gboolean is_open);

Sets whether poppler_annot should initially be displayed open

@@ -2682,6 +3002,82 @@

Since: 0.16

+
+
+

poppler_font_description_copy ()

+
PopplerFontDescription *
+poppler_font_description_copy (PopplerFontDescription *font_desc);
+

Creates a copy of font_desc +

+
+

Parameters

+
+++++ + + + + + +

font_desc

a PopplerFontDescription to copy

 
+
+
+

Returns

+

a new allocated copy of font_desc +

+
+
+
+
+

poppler_font_description_free ()

+
void
+poppler_font_description_free (PopplerFontDescription *font_desc);
+

Frees the given PopplerFontDescription

+
+

Parameters

+
+++++ + + + + + +

font_desc

a PopplerFontDescription

 
+
+
+
+
+

poppler_font_description_new ()

+
PopplerFontDescription *
+poppler_font_description_new (const char *font_name);
+

Creates a new PopplerFontDescriptions

+
+

Parameters

+
+++++ + + + + + +

font_name

the family name of the font

 
+
+
+

Returns

+

a new PopplerFontDescription, use poppler_font_description_free() to free it

+
+

Types and Values

@@ -3207,6 +3603,61 @@


+

PopplerFontDescription

+
typedef struct {
+    char *font_name;
+    double size_pt;
+    PopplerStretch stretch;
+    PopplerWeight weight;
+    PopplerStyle style;
+} PopplerFontDescription;
+
+

A PopplerFontDescription structure represents the description +of a font. When used together with Pango, all the fields are +value-compatible with pango equivalent, although Pango font +descriptions may contain more information.

+

This type supports g_autoptr

+
+

Members

+
+++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

char *font_name;

name of font family

 

double size_pt;

size of font in pt

 

PopplerStretch stretch;

a PopplerStretch representing stretch of the font

 

PopplerWeight weight;

a PopplerWeight representing weight of the font

 

PopplerStyle style;

a PopplerStyle representing style of the font

 
+
+

Since: 24.12.0

+
+
+

POPPLER_ANNOT_TEXT_ICON_CIRCLE

#define POPPLER_ANNOT_TEXT_ICON_CIRCLE "Circle"
 
@@ -3262,6 +3713,6 @@

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Color.html b/glib/reference/html/poppler-Poppler-Color.html index 434c938..f300280 100644 --- a/glib/reference/html/poppler-Poppler-Color.html +++ b/glib/reference/html/poppler-Poppler-Color.html @@ -8,7 +8,7 @@ - + @@ -169,6 +169,7 @@

A PopplerColor describes a RGB color. Color components are values between 0 and 65535

+

Since 24.10 this type supports g_autoptr

Members

@@ -179,17 +180,17 @@

- + - + - + @@ -200,6 +201,6 @@

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Features.html b/glib/reference/html/poppler-Poppler-Features.html index 9e5a10f..e3891fe 100644 --- a/glib/reference/html/poppler-Poppler-Features.html +++ b/glib/reference/html/poppler-Poppler-Features.html @@ -8,7 +8,7 @@ - + @@ -148,7 +148,7 @@

<

Returns

-

TRUE if the version of the poppler header files is the same +

TRUE if the version of the poppler header files is the same as or newer than the passed-in version

Since: 0.12

@@ -187,7 +187,7 @@

<

POPPLER_MAJOR_VERSION

-
#define POPPLER_MAJOR_VERSION (22)
+
#define POPPLER_MAJOR_VERSION (25)
 

The major version number of the poppler header files (e.g. in poppler version 0.1.2 this is 0.)

@@ -205,7 +205,7 @@

<

POPPLER_MINOR_VERSION

-
#define POPPLER_MINOR_VERSION (12)
+
#define POPPLER_MINOR_VERSION (3)
 

The major version number of the poppler header files (e.g. in poppler version 0.1.2 this is 1.)

@@ -252,6 +252,6 @@

<

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Layer.html b/glib/reference/html/poppler-Poppler-Layer.html index 0812fc3..ae1e0dc 100644 --- a/glib/reference/html/poppler-Poppler-Layer.html +++ b/glib/reference/html/poppler-Poppler-Layer.html @@ -8,7 +8,7 @@ - + @@ -42,14 +42,14 @@

- - - - + @@ -200,7 +200,7 @@

Returns

-

TRUE if successful, FALSE (with error +

TRUE if successful, FALSE (with error set) if failed.

Since: 0.14

@@ -208,7 +208,7 @@


poppler_media_get_auto_play ()

-
gboolean
+
gboolean
 poppler_media_get_auto_play (PopplerMedia *poppler_media);

Returns the auto-play parameter.

@@ -228,14 +228,14 @@

Returns

-

TRUE if media should auto-play, FALSE otherwise

+

TRUE if media should auto-play, FALSE otherwise

Since: 20.04.0


poppler_media_get_filename ()

-
const gchar *
+
const gchar *
 poppler_media_get_filename (PopplerMedia *poppler_media);

Returns the media clip filename, in case of non-embedded media. filename might be a local relative or absolute path or a URI

@@ -263,7 +263,7 @@


poppler_media_get_mime_type ()

-
const gchar *
+
const gchar *
 poppler_media_get_mime_type (PopplerMedia *poppler_media);

Returns the media clip mime-type

@@ -290,7 +290,7 @@


poppler_media_get_repeat_count ()

-
gfloat
+
gfloat
 poppler_media_get_repeat_count (PopplerMedia *poppler_media);

Returns the repeat count parameter.

@@ -317,7 +317,7 @@


poppler_media_get_show_controls ()

-
gboolean
+
gboolean
 poppler_media_get_show_controls (PopplerMedia *poppler_media);

Returns the show controls parameter.

@@ -337,18 +337,18 @@

Returns

-

TRUE if media should show controls, FALSE otherwise

+

TRUE if media should show controls, FALSE otherwise

Since: 20.04.0


poppler_media_is_embedded ()

-
gboolean
+
gboolean
 poppler_media_is_embedded (PopplerMedia *poppler_media);
-

Whether the media clip is embedded in the PDF. If the result is TRUE, the embedded stream +

Whether the media clip is embedded in the PDF. If the result is TRUE, the embedded stream can be saved with poppler_media_save() or poppler_media_save_to_callback() function. -If the result is FALSE, the media clip filename can be retrieved with +If the result is FALSE, the media clip filename can be retrieved with poppler_media_get_filename() function.

Parameters

@@ -367,23 +367,23 @@

Returns

-

TRUE if media clip is embedded, FALSE otherwise

+

TRUE if media clip is embedded, FALSE otherwise

Since: 0.14


poppler_media_save ()

-
gboolean
+
gboolean
 poppler_media_save (PopplerMedia *poppler_media,
                     const char *filename,
-                    GError **error);
+ GError **error);

Saves embedded stream of poppler_media to a file indicated by filename . If error - is set, FALSE will be returned. -Possible errors include those in the G_FILE_ERROR domain + is set, FALSE will be returned. +Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates.

Parameters

@@ -406,7 +406,7 @@

- + @@ -414,23 +414,23 @@

Returns

-

TRUE, if the file successfully saved

+

TRUE, if the file successfully saved

Since: 0.14


poppler_media_save_to_fd ()

-
gboolean
+
gboolean
 poppler_media_save_to_fd (PopplerMedia *poppler_media,
                           int fd,
-                          GError **error);
+ GError **error);

Saves embedded stream of poppler_media to a file referred to by fd . If error - is set, FALSE will be returned. -Possible errors include those in the G_FILE_ERROR domain + is set, FALSE will be returned. +Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates. Note that this function takes ownership of fd ; you must not operate on it @@ -456,7 +456,7 @@

- + @@ -464,25 +464,25 @@

Returns

-

TRUE, if the file successfully saved

+

TRUE, if the file successfully saved

Since: 21.12.0


poppler_media_save_to_callback ()

-
gboolean
+
gboolean
 poppler_media_save_to_callback (PopplerMedia *poppler_media,
                                 PopplerMediaSaveFunc save_func,
-                                gpointer user_data,
-                                GError **error);
+ gpointer user_data, + GError **error);

Saves embedded stream of poppler_media by feeding the produced data to save_func . Can be used when you want to store the media clip stream to something other than a file, such as an in-memory buffer or a socket. If error - is set, FALSE will be -returned. Possible errors include those in the G_FILE_ERROR domain and + is set, FALSE will be +returned. Possible errors include those in the G_FILE_ERROR domain and whatever the save function generates.

Parameters

@@ -510,7 +510,7 @@

- + @@ -518,7 +518,7 @@

Returns

-

TRUE, if the save successfully completed

+

TRUE, if the save successfully completed

Since: 0.14

@@ -532,6 +532,6 @@

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Movie.html b/glib/reference/html/poppler-Poppler-Movie.html index 30078d6..d3697fb 100644 --- a/glib/reference/html/poppler-Poppler-Movie.html +++ b/glib/reference/html/poppler-Poppler-Movie.html @@ -8,7 +8,7 @@ - + @@ -50,14 +50,14 @@

- + + + + @@ -523,7 +531,7 @@

P

- - - - @@ -689,9 +693,8 @@

P ├── PopplerPageTransitionType ╰── PopplerSelectionStyle GFlags - ├── PopplerFindFlags - ╰── PopplerPrintFlags - GObject + ╰── PopplerFindFlags + GObject ╰── PopplerPage @@ -984,13 +987,13 @@

P

poppler_page_find_text ()

-
GList *
+
GList *
 poppler_page_find_text (PopplerPage *page,
                         const char *text);

Finds text in page - with the default options (POPPLER_FIND_DEFAULT) and -returns a GList of rectangles for each occurrence of the text on the page. + with the default options (POPPLER_FIND_DEFAULT) and +returns a GList of rectangles for each occurrence of the text on the page. The coordinates are in PDF points.

Parameters

@@ -1016,35 +1019,35 @@

P

Returns

-

a GList of PopplerRectangle,.

+

a GList of PopplerRectangle,.

[element-type PopplerRectangle][transfer full]


poppler_page_find_text_with_options ()

-
GList *
+
GList *
 poppler_page_find_text_with_options (PopplerPage *page,
                                      const char *text,
                                      PopplerFindFlags options);

Finds text in page with the given PopplerFindFlags options and -returns a GList of rectangles for each occurrence of the text on the page. +returns a GList of rectangles for each occurrence of the text on the page. The coordinates are in PDF points.

-

When POPPLER_FIND_MULTILINE is passed in options +

When POPPLER_FIND_MULTILINE is passed in options , matches may span more than one line. In this case, the returned list will contain one PopplerRectangle for each part of a match. The function poppler_rectangle_find_get_match_continued() -will return TRUE for all rectangles belonging to the same match, except for +will return TRUE for all rectangles belonging to the same match, except for the last one. If a hyphen was ignored at the end of the part of the match, -poppler_rectangle_find_get_ignored_hyphen() will return TRUE for that +poppler_rectangle_find_get_ignored_hyphen() will return TRUE for that rectangle.

Note that currently matches spanning more than two lines are not found. (This limitation may be lifted in a future version.)

Note also that currently finding multi-line matches backwards is not -implemented; if you pass POPPLER_FIND_BACKWARDS and POPPLER_FIND_MULTILINE -together, POPPLER_FIND_MULTILINE will be ignored.

+implemented; if you pass POPPLER_FIND_BACKWARDS and POPPLER_FIND_MULTILINE +together, POPPLER_FIND_MULTILINE will be ignored.

Parameters

guint16 red;

guint16 red;

the red component of color

 

guint16 green;

guint16 green;

the green component of color

 

guint16 blue;

guint16 blue;

the blue component of color

 
-gint +gint poppler_layer_get_radio_button_group_id ()
const gchar * +const gchar * poppler_layer_get_title () @@ -65,7 +65,7 @@

-gboolean +gboolean poppler_layer_is_parent () @@ -73,7 +73,7 @@

-gboolean +gboolean poppler_layer_is_visible () @@ -105,7 +105,7 @@

Object Hierarchy

-
    GObject
+
    GObject
     ╰── PopplerLayer
 
@@ -121,7 +121,7 @@

Functions

poppler_layer_get_radio_button_group_id ()

-
gint
+
gint
 poppler_layer_get_radio_button_group_id
                                (PopplerLayer *layer);

Returns the numeric ID the radio button group associated with layer @@ -152,7 +152,7 @@


poppler_layer_get_title ()

-
const gchar *
+
const gchar *
 poppler_layer_get_title (PopplerLayer *layer);

Returns the name of the layer suitable for presentation as a title in a viewer's GUI

@@ -208,7 +208,7 @@


poppler_layer_is_parent ()

-
gboolean
+
gboolean
 poppler_layer_is_parent (PopplerLayer *layer);

Returns whether layer is parent of other nested layers.

@@ -229,7 +229,7 @@

Returns

-

TRUE if layer +

TRUE if layer is a parent layer

Since: 0.12

@@ -237,7 +237,7 @@


poppler_layer_is_visible ()

-
gboolean
+
gboolean
 poppler_layer_is_visible (PopplerLayer *layer);

Returns whether layer is visible

@@ -258,7 +258,7 @@

Returns

-

TRUE if layer +

TRUE if layer is visible

Since: 0.12

@@ -297,6 +297,6 @@

+
Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Media.html b/glib/reference/html/poppler-Poppler-Media.html index 6b30a45..bac7ebe 100644 --- a/glib/reference/html/poppler-Poppler-Media.html +++ b/glib/reference/html/poppler-Poppler-Media.html @@ -8,7 +8,7 @@ - + @@ -42,7 +42,7 @@

-gboolean +gboolean (*PopplerMediaSaveFunc) () @@ -50,21 +50,21 @@

-gboolean +gboolean poppler_media_get_auto_play ()
const gchar * +const gchar * poppler_media_get_filename ()
const gchar * +const gchar * poppler_media_get_mime_type () @@ -72,7 +72,7 @@

-gfloat +gfloat poppler_media_get_repeat_count () @@ -80,7 +80,7 @@

-gboolean +gboolean poppler_media_get_show_controls () @@ -88,7 +88,7 @@

-gboolean +gboolean poppler_media_is_embedded () @@ -96,7 +96,7 @@

-gboolean +gboolean poppler_media_save () @@ -104,7 +104,7 @@

-gboolean +gboolean poppler_media_save_to_fd () @@ -112,7 +112,7 @@

-gboolean +gboolean poppler_media_save_to_callback () @@ -136,7 +136,7 @@

Object Hierarchy

-
    GObject
+
    GObject
     ╰── PopplerMedia
 
@@ -152,17 +152,17 @@

Functions

PopplerMediaSaveFunc ()

-
gboolean
-(*PopplerMediaSaveFunc) (const gchar *buf,
-                         gsize count,
-                         gpointer data,
-                         GError **error);
+
gboolean
+(*PopplerMediaSaveFunc) (const gchar *buf,
+                         gsize count,
+                         gpointer data,
+                         GError **error);

Specifies the type of the function passed to poppler_media_save_to_callback(). It is called once for each block of bytes that is "written" by poppler_media_save_to_callback(). If -successful it should return TRUE. If an error occurs it should set +successful it should return TRUE. If an error occurs it should set error - and return FALSE, in which case poppler_media_save_to_callback() + and return FALSE, in which case poppler_media_save_to_callback() will fail with the same error.

Parameters

@@ -192,7 +192,7 @@

error

GError to set on error, or NULL

GError to set on error, or NULL

 

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]

error

return location for error, or NULL.

return location for error, or NULL.

[allow-none]
-guint64 +guint64 poppler_movie_get_duration ()
const gchar * +const gchar * poppler_movie_get_filename () @@ -73,7 +73,7 @@

-gdouble +gdouble poppler_movie_get_rate () @@ -81,7 +81,7 @@

-gushort +gushort poppler_movie_get_rotation_angle () @@ -89,7 +89,7 @@

-guint64 +guint64 poppler_movie_get_start () @@ -97,7 +97,7 @@

-gdouble +gdouble poppler_movie_get_volume () @@ -105,7 +105,7 @@

-gboolean +gboolean poppler_movie_is_synchronous () @@ -113,7 +113,7 @@

-gboolean +gboolean poppler_movie_need_poster () @@ -121,7 +121,7 @@

-gboolean +gboolean poppler_movie_show_controls () @@ -153,7 +153,7 @@

Object Hierarchy

    GEnum
     ╰── PopplerMoviePlayMode
-    GObject
+    GObject
     ╰── PopplerMovie
 
@@ -171,8 +171,8 @@

poppler_movie_get_aspect ()

void
 poppler_movie_get_aspect (PopplerMovie *poppler_movie,
-                          gint *width,
-                          gint *height);
+ gint *width, + gint *height);

Returns the dimensions of the movie's bounding box (in pixels). The respective PDF movie dictionary entry is optional; if missing, -1x-1 will be returned.

@@ -208,7 +208,7 @@


poppler_movie_get_duration ()

-
guint64
+
guint64
 poppler_movie_get_duration (PopplerMovie *poppler_movie);

Returns the duration of the movie playback

@@ -235,7 +235,7 @@


poppler_movie_get_filename ()

-
const gchar *
+
const gchar *
 poppler_movie_get_filename (PopplerMovie *poppler_movie);

Returns the local filename identifying a self-describing movie file

@@ -291,7 +291,7 @@


poppler_movie_get_rate ()

-
gdouble
+
gdouble
 poppler_movie_get_rate (PopplerMovie *poppler_movie);

Returns the relative speed of the movie

@@ -318,7 +318,7 @@


poppler_movie_get_rotation_angle ()

-
gushort
+
gushort
 poppler_movie_get_rotation_angle (PopplerMovie *poppler_movie);

Returns the rotation angle

@@ -346,7 +346,7 @@


poppler_movie_get_start ()

-
guint64
+
guint64
 poppler_movie_get_start (PopplerMovie *poppler_movie);

Returns the start position of the movie playback

@@ -373,7 +373,7 @@


poppler_movie_get_volume ()

-
gdouble
+
gdouble
 poppler_movie_get_volume (PopplerMovie *poppler_movie);

Returns the playback audio volume

@@ -400,7 +400,7 @@


poppler_movie_is_synchronous ()

-
gboolean
+
gboolean
 poppler_movie_is_synchronous (PopplerMovie *poppler_movie);

Returns whether the user must wait for the movie to be finished before the PDF viewer accepts any interactive action

@@ -421,14 +421,14 @@

Returns

-

TRUE if yes, FALSE otherwise

+

TRUE if yes, FALSE otherwise

Since: 0.80


poppler_movie_need_poster ()

-
gboolean
+
gboolean
 poppler_movie_need_poster (PopplerMovie *poppler_movie);

Returns whether a poster image representing the Movie shall be displayed. The poster image must be retrieved @@ -450,14 +450,14 @@

Returns

-

TRUE if move needs a poster image, FALSE otherwise

+

TRUE if move needs a poster image, FALSE otherwise

Since: 0.14


poppler_movie_show_controls ()

-
gboolean
+
gboolean
 poppler_movie_show_controls (PopplerMovie *poppler_movie);

Returns whether to display a movie controller bar while playing the movie

@@ -477,7 +477,7 @@

Returns

-

TRUE if controller bar should be displayed, FALSE otherwise

+

TRUE if controller bar should be displayed, FALSE otherwise

Since: 0.14

@@ -538,6 +538,6 @@

+
Generated by GTK-Doc V1.34.0

\ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Page.html b/glib/reference/html/poppler-Poppler-Page.html index 8120a65..ffe4205 100644 --- a/glib/reference/html/poppler-Poppler-Page.html +++ b/glib/reference/html/poppler-Poppler-Page.html @@ -8,7 +8,7 @@ - + @@ -147,7 +147,7 @@

P

-GList * +GList * poppler_page_find_text () @@ -155,7 +155,7 @@

P

-GList * +GList * poppler_page_find_text_with_options () @@ -203,7 +203,7 @@

P

-GList * +GList * poppler_page_get_annot_mapping () @@ -211,7 +211,7 @@

P

-gboolean +gboolean poppler_page_get_bounding_box () @@ -235,7 +235,7 @@

P

-GList * +GList * poppler_page_get_form_field_mapping () @@ -243,7 +243,7 @@

P

-cairo_surface_t * +cairo_surface_t * poppler_page_get_image () @@ -251,7 +251,7 @@

P

-GList * +GList * poppler_page_get_image_mapping () @@ -267,7 +267,7 @@

P

-gchar * +gchar * poppler_page_get_label () @@ -275,7 +275,7 @@

P

-GList * +GList * poppler_page_get_link_mapping () @@ -283,7 +283,7 @@

P

-cairo_region_t * +cairo_region_t * poppler_page_get_selected_region () @@ -299,7 +299,7 @@

P

-GList * +GList * poppler_page_get_selection_region () @@ -323,7 +323,7 @@

P

-GList * +GList * poppler_page_get_text_attributes () @@ -331,7 +331,7 @@

P

-GList * +GList * poppler_page_get_text_attributes_for_area () @@ -347,7 +347,7 @@

P

-gboolean +gboolean poppler_page_get_text_layout () @@ -355,7 +355,7 @@

P

-gboolean +gboolean poppler_page_get_text_layout_for_area () @@ -363,7 +363,7 @@

P

-cairo_surface_t * +cairo_surface_t * poppler_page_get_thumbnail () @@ -371,7 +371,7 @@

P

-gboolean +gboolean poppler_page_get_thumbnail_size () @@ -406,6 +406,14 @@

P void

+poppler_page_render_full () +
+void + poppler_page_render_for_printing ()
-gboolean +gboolean poppler_rectangle_find_get_match_continued () @@ -531,7 +539,7 @@

P

-gboolean +gboolean poppler_rectangle_find_get_ignored_hyphen () @@ -649,10 +657,6 @@

P

PopplerPoint
enumPopplerPrintFlags
  PopplerQuadrilateral
@@ -1075,7 +1078,7 @@

P

Returns

a newly allocated list -of newly allocated PopplerRectangle. Free with g_list_free_full() using poppler_rectangle_free().

+of newly allocated PopplerRectangle. Free with g_list_free_full() using poppler_rectangle_free().

[element-type PopplerRectangle][transfer full]

Since: 0.22

@@ -1084,11 +1087,11 @@

P

poppler_page_free_annot_mapping ()

void
-poppler_page_free_annot_mapping (GList *list);
+poppler_page_free_annot_mapping (GList *list);

Frees a list of PopplerAnnotMappings allocated by poppler_page_get_annot_mapping(). It also unreferences the PopplerAnnots that each mapping contains, so if you want to keep them around, you need to -reference them with g_object_ref().

+reference them with g_object_ref().

Parameters

@@ -1110,7 +1113,7 @@

P

poppler_page_free_form_field_mapping ()

void
-poppler_page_free_form_field_mapping (GList *list);
+poppler_page_free_form_field_mapping (GList *list);

Frees a list of PopplerFormFieldMappings allocated by poppler_page_get_form_field_mapping().

@@ -1134,7 +1137,7 @@

P

poppler_page_free_image_mapping ()

void
-poppler_page_free_image_mapping (GList *list);
+poppler_page_free_image_mapping (GList *list);

Frees a list of PopplerImageMappings allocated by poppler_page_get_image_mapping().

@@ -1158,7 +1161,7 @@

P

poppler_page_free_link_mapping ()

void
-poppler_page_free_link_mapping (GList *list);
+poppler_page_free_link_mapping (GList *list);

Frees a list of PopplerLinkMappings allocated by poppler_page_get_link_mapping(). It also frees the PopplerActions that each mapping contains, so if you want to keep them around, you need to @@ -1184,7 +1187,7 @@

P

poppler_page_free_text_attributes ()

void
-poppler_page_free_text_attributes (GList *list);
+poppler_page_free_text_attributes (GList *list);

Frees a list of PopplerTextAttributess allocated by poppler_page_get_text_attributes().

@@ -1208,7 +1211,7 @@

P

poppler_page_get_annot_mapping ()

-
GList *
+
GList *
 poppler_page_get_annot_mapping (PopplerPage *page);

Returns a list of PopplerAnnotMapping items that map from a location on page @@ -1231,14 +1234,14 @@

P

Returns

-

A GList of PopplerAnnotMapping.

+

A GList of PopplerAnnotMapping.

[element-type PopplerAnnotMapping][transfer full]


poppler_page_get_bounding_box ()

-
gboolean
+
gboolean
 poppler_page_get_bounding_box (PopplerPage *page,
                                PopplerRectangle *rect);
@@ -1304,7 +1307,7 @@

P

poppler_page_get_form_field_mapping ()

-
GList *
+
GList *
 poppler_page_get_form_field_mapping (PopplerPage *page);

Returns a list of PopplerFormFieldMapping items that map from a location on page @@ -1327,16 +1330,16 @@

P

Returns

-

A GList of PopplerFormFieldMapping.

+

A GList of PopplerFormFieldMapping.

[element-type PopplerFormFieldMapping][transfer full]


poppler_page_get_image ()

-
cairo_surface_t *
+
cairo_surface_t *
 poppler_page_get_image (PopplerPage *page,
-                        gint image_id);
+ gint image_id);

Returns a cairo surface for the image of the page

@@ -1369,7 +1372,7 @@

P

poppler_page_get_image_mapping ()

-
GList *
+
GList *
 poppler_page_get_image_mapping (PopplerPage *page);

Returns a list of PopplerImageMapping items that map from a location on page @@ -1392,7 +1395,7 @@

P

Returns

-

A GList of PopplerImageMapping.

+

A GList of PopplerImageMapping.

[element-type PopplerImageMapping][transfer full]

@@ -1427,7 +1430,7 @@

P

poppler_page_get_label ()

-
gchar *
+
gchar *
 poppler_page_get_label (PopplerPage *page);

Returns the label of page . Note that page labels @@ -1451,7 +1454,7 @@

P

Returns

a new allocated string containing the label of page , -or NULL if page +or NULL if page doesn't have a label

Since: 0.16

@@ -1459,7 +1462,7 @@

P

poppler_page_get_link_mapping ()

-
GList *
+
GList *
 poppler_page_get_link_mapping (PopplerPage *page);

Returns a list of PopplerLinkMapping items that map from a location on page @@ -1482,21 +1485,21 @@

P

Returns

-

A GList of PopplerLinkMapping.

+

A GList of PopplerLinkMapping.

[element-type PopplerLinkMapping][transfer full]


poppler_page_get_selected_region ()

-
cairo_region_t *
+
cairo_region_t *
 poppler_page_get_selected_region (PopplerPage *page,
-                                  gdouble scale,
+                                  gdouble scale,
                                   PopplerSelectionStyle style,
                                   PopplerRectangle *selection);

Returns a region containing the area that would be rendered by poppler_page_render_selection(). -The returned region must be freed with cairo_region_destroy()

+The returned region must be freed with cairo_region_destroy()

Parameters

@@ -1574,17 +1577,19 @@

P

Returns

-

a pointer to the contents of the selection -as a string

+

a pointer to the contents of the +selection +as a string.

+

[transfer full]

Since: 0.16


poppler_page_get_selection_region ()

-
GList *
+
GList *
 poppler_page_get_selection_region (PopplerPage *page,
-                                   gdouble scale,
+                                   gdouble scale,
                                    PopplerSelectionStyle style,
                                    PopplerRectangle *selection);
@@ -1592,7 +1597,7 @@

P

Use poppler_page_get_selected_region() instead.

Returns a region containing the area that would be rendered by -poppler_page_render_selection() as a GList of +poppler_page_render_selection() as a GList of PopplerRectangle. The returned list must be freed with poppler_page_selection_region_free().

@@ -1629,7 +1634,7 @@

P

Returns

-

a GList of PopplerRectangle.

+

a GList of PopplerRectangle.

[element-type PopplerRectangle][transfer full]

@@ -1704,9 +1709,9 @@

P

poppler_page_get_text_attributes ()

-
GList *
+
GList *
 poppler_page_get_text_attributes (PopplerPage *page);
-

Obtains the attributes of the text as a GList of PopplerTextAttributes. +

Obtains the attributes of the text as a GList of PopplerTextAttributes. This list must be freed with poppler_page_free_text_attributes() when done.

Each list element is a PopplerTextAttributes struct where start_index and end_index indicates the range of text (as returned by poppler_page_get_text()) @@ -1729,7 +1734,7 @@

P

Returns

-

A GList of PopplerTextAttributes.

+

A GList of PopplerTextAttributes.

[element-type PopplerTextAttributes][transfer full]

Since: 0.18

@@ -1737,12 +1742,12 @@

P

poppler_page_get_text_attributes_for_area ()

-
GList *
+
GList *
 poppler_page_get_text_attributes_for_area
                                (PopplerPage *page,
                                 PopplerRectangle *area);

Obtains the attributes of the text in area - as a GList of PopplerTextAttributes. + as a GList of PopplerTextAttributes. This list must be freed with poppler_page_free_text_attributes() when done.

Each list element is a PopplerTextAttributes struct where start_index and end_index indicates the range of text (as returned by poppler_page_get_text_for_area()) @@ -1771,7 +1776,7 @@

P

Returns

-

A GList of PopplerTextAttributes.

+

A GList of PopplerTextAttributes.

[element-type PopplerTextAttributes][transfer full]

Since: 0.26

@@ -1816,12 +1821,12 @@

P

poppler_page_get_text_layout ()

-
gboolean
+
gboolean
 poppler_page_get_text_layout (PopplerPage *page,
                               PopplerRectangle **rectangles,
-                              guint *n_rectangles);
+ guint *n_rectangles);

Obtains the layout of the text as a list of PopplerRectangle -This array must be freed with g_free() when done.

+This array must be freed with g_free() when done.

The position in the array represents an offset in the text returned by poppler_page_get_text()

See also poppler_page_get_text_layout_for_area().

@@ -1854,21 +1859,21 @@

P

Returns

-

TRUE if the page contains text, FALSE otherwise

+

TRUE if the page contains text, FALSE otherwise

Since: 0.16


poppler_page_get_text_layout_for_area ()

-
gboolean
+
gboolean
 poppler_page_get_text_layout_for_area (PopplerPage *page,
                                        PopplerRectangle *area,
                                        PopplerRectangle **rectangles,
-                                       guint *n_rectangles);
+ guint *n_rectangles);

Obtains the layout of the text contained in area as a list of PopplerRectangle -This array must be freed with g_free() when done.

+This array must be freed with g_free() when done.

The position in the array represents an offset in the text returned by poppler_page_get_text_for_area()

@@ -1905,18 +1910,18 @@

P

Returns

-

TRUE if the page contains text, FALSE otherwise

+

TRUE if the page contains text, FALSE otherwise

Since: 0.26


poppler_page_get_thumbnail ()

-
cairo_surface_t *
+
cairo_surface_t *
 poppler_page_get_thumbnail (PopplerPage *page);

Get the embedded thumbnail for the specified page. If the document doesn't have an embedded thumbnail for the page, this function -returns NULL.

+returns NULL.

Parameters

@@ -1934,18 +1939,18 @@

P

Returns

-

the tumbnail as a cairo_surface_t or NULL if the document +

the tumbnail as a cairo_surface_t or NULL if the document doesn't have a thumbnail for this page.


poppler_page_get_thumbnail_size ()

-
gboolean
+
gboolean
 poppler_page_get_thumbnail_size (PopplerPage *page,
                                  int *width,
                                  int *height);
-

Returns TRUE if page +

Returns TRUE if page has a thumbnail associated with it. It also fills in width and height @@ -1981,7 +1986,7 @@

P

Returns

-

TRUE, if page +

TRUE, if page has a thumbnail associated with it.

@@ -2009,7 +2014,7 @@

P

Returns

-

a PopplerPageTransition or NULL.

+

a PopplerPageTransition or NULL.


@@ -2050,7 +2055,7 @@

P

poppler_page_render ()

void
 poppler_page_render (PopplerPage *page,
-                     cairo_t *cairo);
+ cairo_t *cairo);

Render the page to the given cairo context. This function is for rendering a page that will be displayed. If you want to render a page that will be printed use @@ -2082,13 +2087,63 @@

P
+

poppler_page_render_full ()

+
void
+poppler_page_render_full (PopplerPage *page,
+                          cairo_t *cairo,
+                          gboolean printing,
+                          PopplerRenderAnnotsFlags flags);
+

Render the page to the given cairo context, manually selecting which +annotations should be displayed.

+

The printing + parameter determines whether a page is rendered for printing +or for displaying it on a screen. See the documentation for +poppler_page_render_for_printing() for the differences between rendering to +the screen and rendering to a printer.

+
+

Parameters

+

+++++ + + + + + + + + + + + + + + + + + + + + + + +

page

the page to render from

 

cairo

cairo context to render to

 

printing

cairo context to render to

 

flags

flags which allow to select which annotations to render

 
+
+

Since: 25.02

+

+
+

poppler_page_render_for_printing ()

void
 poppler_page_render_for_printing (PopplerPage *page,
-                                  cairo_t *cairo);
+ cairo_t *cairo);

Render the page to the given cairo context for printing with POPPLER_PRINT_ALL flags selected. If you want a different set of flags, -use poppler_page_render_for_printing_with_options().

+use poppler_page_render_full() with printing TRUE and the corresponding +flags.

The difference between poppler_page_render() and this function is that some things get rendered differently between screens and printers:

    @@ -2142,8 +2197,12 @@

    P
    void
     poppler_page_render_for_printing_with_options
                                    (PopplerPage *page,
    -                                cairo_t *cairo,
    -                                PopplerPrintFlags options);
    + cairo_t *cairo, + PopplerPrintFlags options); +
    +

    poppler_page_render_for_printing_with_options has been deprecated since version 25.02 and should not be used in newly-written code.

    +

    Use poppler_page_render_full() instead.

    +

    Render the page to the given cairo context for printing with the specified options

    See the documentation for poppler_page_render_for_printing() for the @@ -2182,7 +2241,7 @@

    P

    poppler_page_render_selection ()

    void
     poppler_page_render_selection (PopplerPage *page,
    -                               cairo_t *cairo,
    +                               cairo_t *cairo,
                                    PopplerRectangle *selection,
                                    PopplerRectangle *old_selection,
                                    PopplerSelectionStyle style,
    @@ -2283,13 +2342,13 @@ 

    P

    poppler_page_selection_region_free ()

    void
    -poppler_page_selection_region_free (GList *region);
    +poppler_page_selection_region_free (GList *region);

    poppler_page_selection_region_free has been deprecated since version 0.16 and should not be used in newly-written code.

    Use only to free deprecated regions created by poppler_page_get_selection_region(). Regions created by poppler_page_get_selected_region() should be freed with -cairo_region_destroy() instead.

    +cairo_region_destroy() instead.

    Frees region

    @@ -2303,7 +2362,7 @@

    P

    region

    -

    a GList of +

    a GList of PopplerRectangle.

    [element-type PopplerRectangle] @@ -2536,14 +2595,14 @@

    P

    poppler_rectangle_find_get_match_continued ()

    -
    gboolean
    +
    gboolean
     poppler_rectangle_find_get_match_continued
                                    (const PopplerRectangle *rectangle);

    When using poppler_page_find_text_with_options() with the -POPPLER_FIND_MULTILINE flag, a match may span more than one line +POPPLER_FIND_MULTILINE flag, a match may span more than one line and thus consist of more than one rectangle. Every rectangle belonging -to the same match will return TRUE from this function, except for -the last rectangle, where this function will return FALSE.

    +to the same match will return TRUE from this function, except for +the last rectangle, where this function will return FALSE.

    Note that you must only call this function on a PopplerRectangle returned in the list from poppler_page_find_text() or poppler_page_find_text_with_options().

    @@ -2571,16 +2630,16 @@

    P

    poppler_rectangle_find_get_ignored_hyphen ()

    -
    gboolean
    +
    gboolean
     poppler_rectangle_find_get_ignored_hyphen
                                    (const PopplerRectangle *rectangle);

    When using poppler_page_find_text_with_options() with the -POPPLER_FIND_MULTILINE flag, a match may span more than one line, +POPPLER_FIND_MULTILINE flag, a match may span more than one line, and may have been formed by ignoring a hyphen at the end of the line. When this happens at the end of the line corresponding to rectangle , -this function returns TRUE (and then poppler_rectangle_find_get_match_continued() -will also return TRUE); otherwise it returns FALSE.

    +this function returns TRUE (and then poppler_rectangle_find_get_match_continued() +will also return TRUE); otherwise it returns FALSE.

    Note that you must only call this function on a PopplerRectangle returned in the list from poppler_page_find_text() or poppler_page_find_text_with_options().

    @@ -2719,6 +2778,7 @@

    P

    A PopplerAnnotMapping structure represents the location of annot on the page

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -2756,51 +2816,24 @@

    P

    - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + +

    POPPLER_FIND_DEFAULT

    -

    use default search settings

    -
     

    POPPLER_FIND_CASE_SENSITIVE

    -

    do case sensitive search

    -
     

    POPPLER_FIND_BACKWARDS

    -

    search backwards

    -
     

    POPPLER_PRINT_DOCUMENT

      

    POPPLER_FIND_WHOLE_WORDS_ONLY

    -

    search only whole words

    -
     

    POPPLER_PRINT_MARKUP_ANNOTS

      

    POPPLER_FIND_IGNORE_DIACRITICS

    -

    do diacritics insensitive search, -i.e. ignore accents, umlauts, diaeresis,etc. while matching. This -option will be ignored if the search term is not pure ascii. Since 0.73.

    -
     

    POPPLER_PRINT_STAMP_ANNOTS_ONLY

      

    POPPLER_FIND_MULTILINE

    -

    allows to match on text spanning from -end of a line to the next line. (Currently it won't match on text spanning -more than two lines.) Automatically ignores hyphen at end of line, and -allows whitespace in search term to match on newline char. Since: 21.05.0.

    -
     

    POPPLER_PRINT_ALL

      
    @@ -2818,6 +2851,7 @@

    P

    A PopplerFormFieldMapping structure represents the location of field on the page

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -2851,6 +2885,7 @@

    P

    A PopplerImageMapping structure represents the location of an image on the page

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -2866,7 +2901,7 @@

    P

    - + @@ -2885,6 +2920,7 @@

    P

    A PopplerLinkMapping structure represents the location of action on the page

    +

    Since 24.10 this type supports g_autoptr

    Members

     

    gint image_id;

    gint image_id;

    an image identifier

     
    @@ -2929,6 +2965,7 @@

    P

    A PopplerPageTransition structures describes a visual transition to use when moving between pages during a presentation

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -2957,12 +2994,12 @@

    P

    - + - + - + - + - + @@ -3169,6 +3206,7 @@

    P } PopplerPoint;

    A PopplerPoint is used to describe a location point on a page

    +

    Since 24.10 this type supports g_autoptr

    Members

     

    gint duration;

    gint duration;

    the duration of the transition effect

     

    gint angle;

    gint angle;

    the direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. Only for POPPLER_PAGE_TRANSITION_WIPE, POPPLER_PAGE_TRANSITION_GLITTER, POPPLER_PAGE_TRANSITION_FLY, @@ -2971,19 +3008,19 @@

    P

     

    gdouble scale;

    gdouble scale;

    the starting or ending scale at which the changes shall be drawn. Only for POPPLER_PAGE_TRANSITION_FLY transition type

     

    gboolean rectangular;

    gboolean rectangular;

    whether the area that will be flown is rectangular and opaque. Only for POPPLER_PAGE_TRANSITION_FLY transition type

     

    gdouble duration_real;

    gdouble duration_real;

       
    @@ -3179,12 +3217,12 @@

    P

    - + - + @@ -3194,52 +3232,6 @@

    P
    -

    enum PopplerPrintFlags

    -

    Printing flags

    -
    -

    Members

    -

    gdouble x;

    gdouble x;

    x coordinate

     

    gdouble y;

    gdouble y;

    y coordinate

     
    ----- - - - - - - - - - - - - - - - - - - - - - - -

    POPPLER_PRINT_DOCUMENT

    -

    print main document contents

    -
     

    POPPLER_PRINT_MARKUP_ANNOTS

    -

    print document and markup annotations

    -
     

    POPPLER_PRINT_STAMP_ANNOTS_ONLY

    -

    print document and only stamp annotations

    -
     

    POPPLER_PRINT_ALL

    -

    print main document contents and all markup annotations

    -
     
    -
    -

    Since: 0.16

    -

    -
    -

    PopplerQuadrilateral

    typedef struct {
         PopplerPoint p1;
    @@ -3250,6 +3242,7 @@ 

    P

    A PopplerQuadrilateral is used to describe rectangle-like polygon with arbitrary inclination on a page.

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -3296,6 +3289,7 @@

    P

    A PopplerRectangle is used to describe locations on a page and bounding boxes

    +

    Since 24.10 this type supports g_autoptr

    Members

    @@ -3306,22 +3300,22 @@

    P

    - + - + - + - + @@ -3391,17 +3385,17 @@

    P

    - + - + - + @@ -3411,12 +3405,12 @@

    P

    - + - + @@ -3431,7 +3425,7 @@

    P

    The “label” property

      “label”                    char *
    -

    The label of the page or NULL. See also poppler_page_get_label()

    +

    The label of the page or NULL. See also poppler_page_get_label()

    Owner: PopplerPage

    Flags: Read

    Default value: NULL

    @@ -3439,6 +3433,6 @@

    P

    +
    Generated by GTK-Doc V1.34.0 \ No newline at end of file diff --git a/glib/reference/html/poppler-Poppler-Text-Span.html b/glib/reference/html/poppler-Poppler-Text-Span.html index 2463d9d..c7a2dec 100644 --- a/glib/reference/html/poppler-Poppler-Text-Span.html +++ b/glib/reference/html/poppler-Poppler-Text-Span.html @@ -8,7 +8,7 @@ - + @@ -65,14 +65,14 @@

    - -

    gdouble x1;

    gdouble x1;

    x coordinate of lower left corner

     

    gdouble y1;

    gdouble y1;

    y coordinate of lower left corner

     

    gdouble x2;

    gdouble x2;

    x coordinate of upper right corner

     

    gdouble y2;

    gdouble y2;

    y coordinate of upper right corner

     

    gchar *font_name;

    gchar *font_name;

    font name

     

    gdouble font_size;

    gdouble font_size;

    font size

     

    gboolean is_underlined;

    gboolean is_underlined;

    if text is underlined

     
     

    gint start_index;

    gint start_index;

    start position this text attributes apply

     

    gint end_index;

    gint end_index;

    end position this text attributes apply

     
    const gchar * +const gchar * poppler_text_span_get_font_name ()
    const gchar * +const gchar * poppler_text_span_get_text () @@ -80,7 +80,7 @@

    -gboolean +gboolean poppler_text_span_is_bold_font () @@ -88,7 +88,7 @@

    -gboolean +gboolean poppler_text_span_is_fixed_width_font () @@ -96,7 +96,7 @@

    -gboolean +gboolean poppler_text_span_is_serif_font () @@ -218,7 +218,7 @@


    poppler_text_span_get_font_name ()

    -
    const gchar *
    +
    const gchar *
     poppler_text_span_get_font_name (PopplerTextSpan *poppler_text_span);

    Obtains the name of the font in which the span is to be rendered.

    @@ -239,7 +239,7 @@

    Returns

    A string containing the font name, or -NULL if a font is not defined.

    +NULL if a font is not defined.

    [transfer none]

    Since: 0.26

    @@ -247,7 +247,7 @@


    poppler_text_span_get_text ()

    -
    const gchar *
    +
    const gchar *
     poppler_text_span_get_text (PopplerTextSpan *poppler_text_span);

    Obtains the text contained in the span.

    @@ -275,7 +275,7 @@


    poppler_text_span_is_bold_font ()

    -
    gboolean
    +
    gboolean
     poppler_text_span_is_bold_font (PopplerTextSpan *poppler_text_span);

    Check whether a text span is meant to be rendered using a bold font.

    @@ -302,7 +302,7 @@


    poppler_text_span_is_fixed_width_font ()

    -
    gboolean
    +
    gboolean
     poppler_text_span_is_fixed_width_font (PopplerTextSpan *poppler_text_span);

    Check wether a text span is meant to be rendered using a fixed-width font.

    @@ -329,7 +329,7 @@


    poppler_text_span_is_serif_font ()

    -
    gboolean
    +
    gboolean
     poppler_text_span_is_serif_font (PopplerTextSpan *poppler_text_span);

    Check whether a text span is meant to be rendered using a serif font.

    @@ -359,10 +359,11 @@

    PopplerTextSpan

    typedef struct _PopplerTextSpan PopplerTextSpan;
    +

    Since 24.10 this type supports g_autoptr

    +
    Generated by GTK-Doc V1.34.0

    \ No newline at end of file diff --git a/glib/reference/html/poppler.devhelp2 b/glib/reference/html/poppler.devhelp2 index faae5a9..794856c 100644 --- a/glib/reference/html/poppler.devhelp2 +++ b/glib/reference/html/poppler.devhelp2 @@ -225,8 +225,9 @@ + - + @@ -258,7 +259,6 @@ - @@ -297,6 +297,14 @@ + + + + + + + + @@ -336,12 +344,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -360,7 +418,12 @@ + + + + + @@ -368,6 +431,7 @@ + @@ -391,6 +455,7 @@ + @@ -410,6 +475,9 @@ + + + @@ -430,6 +498,7 @@ + @@ -650,12 +719,10 @@ - - - - - - + + + + @@ -672,10 +739,6 @@ - - - - @@ -937,6 +1000,7 @@ + @@ -1030,6 +1094,11 @@ + + + + + diff --git a/glib/reference/html/style.css b/glib/reference/html/style.css index 2eeda1f..7ae248c 100644 --- a/glib/reference/html/style.css +++ b/glib/reference/html/style.css @@ -462,9 +462,9 @@ td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5 span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .hll { background-color: #ffffcc } .c { color: #3D7B7B; font-style: italic } /* Comment */ -.err { border: 1px solid #FF0000 } /* Error */ +.err { border: 1px solid #F00 } /* Error */ .k { color: #008000; font-weight: bold } /* Keyword */ -.o { color: #666666 } /* Operator */ +.o { color: #666 } /* Operator */ .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ .cp { color: #9C6500 } /* Comment.Preproc */ @@ -473,6 +473,7 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ .gd { color: #A00000 } /* Generic.Deleted */ .ge { font-style: italic } /* Generic.Emph */ +.ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .gr { color: #E40000 } /* Generic.Error */ .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .gi { color: #008400 } /* Generic.Inserted */ @@ -480,34 +481,34 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .gs { font-weight: bold } /* Generic.Strong */ .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #0044DD } /* Generic.Traceback */ +.gt { color: #04D } /* Generic.Traceback */ .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ .kp { color: #008000 } /* Keyword.Pseudo */ .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .kt { color: #B00040 } /* Keyword.Type */ -.m { color: #666666 } /* Literal.Number */ +.m { color: #666 } /* Literal.Number */ .s { color: #BA2121 } /* Literal.String */ .na { color: #687822 } /* Name.Attribute */ .nb { color: #008000 } /* Name.Builtin */ -.nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.no { color: #880000 } /* Name.Constant */ -.nd { color: #AA22FF } /* Name.Decorator */ +.nc { color: #00F; font-weight: bold } /* Name.Class */ +.no { color: #800 } /* Name.Constant */ +.nd { color: #A2F } /* Name.Decorator */ .ni { color: #717171; font-weight: bold } /* Name.Entity */ .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ -.nf { color: #0000FF } /* Name.Function */ +.nf { color: #00F } /* Name.Function */ .nl { color: #767600 } /* Name.Label */ -.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.nn { color: #00F; font-weight: bold } /* Name.Namespace */ .nt { color: #008000; font-weight: bold } /* Name.Tag */ .nv { color: #19177C } /* Name.Variable */ -.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mb { color: #666666 } /* Literal.Number.Bin */ -.mf { color: #666666 } /* Literal.Number.Float */ -.mh { color: #666666 } /* Literal.Number.Hex */ -.mi { color: #666666 } /* Literal.Number.Integer */ -.mo { color: #666666 } /* Literal.Number.Oct */ +.ow { color: #A2F; font-weight: bold } /* Operator.Word */ +.w { color: #BBB } /* Text.Whitespace */ +.mb { color: #666 } /* Literal.Number.Bin */ +.mf { color: #666 } /* Literal.Number.Float */ +.mh { color: #666 } /* Literal.Number.Hex */ +.mi { color: #666 } /* Literal.Number.Integer */ +.mo { color: #666 } /* Literal.Number.Oct */ .sa { color: #BA2121 } /* Literal.String.Affix */ .sb { color: #BA2121 } /* Literal.String.Backtick */ .sc { color: #BA2121 } /* Literal.String.Char */ @@ -522,9 +523,9 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: .s1 { color: #BA2121 } /* Literal.String.Single */ .ss { color: #19177C } /* Literal.String.Symbol */ .bp { color: #008000 } /* Name.Builtin.Pseudo */ -.fm { color: #0000FF } /* Name.Function.Magic */ +.fm { color: #00F } /* Name.Function.Magic */ .vc { color: #19177C } /* Name.Variable.Class */ .vg { color: #19177C } /* Name.Variable.Global */ .vi { color: #19177C } /* Name.Variable.Instance */ .vm { color: #19177C } /* Name.Variable.Magic */ -.il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file +.il { color: #666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/glib/reference/poppler-sections.txt b/glib/reference/poppler-sections.txt index f027cd0..86f7e36 100644 --- a/glib/reference/poppler-sections.txt +++ b/glib/reference/poppler-sections.txt @@ -77,6 +77,7 @@ poppler_page_get_thumbnail_size poppler_page_get_transition poppler_page_remove_annot poppler_page_render +poppler_page_render_full poppler_page_render_for_printing poppler_page_render_for_printing_with_options poppler_page_render_selection @@ -366,6 +367,7 @@ poppler_attachment_get_type PopplerFormField PopplerFormField PopplerAdditionalActionType +PopplerCertificateInfo PopplerCertificateStatus PopplerFormFieldType PopplerFormButtonType @@ -374,6 +376,14 @@ PopplerFormTextType PopplerSignatureInfo PopplerSignatureStatus PopplerSignatureValidationFlags +poppler_certificate_info_get_expiration_time +poppler_certificate_info_get_issuance_time +poppler_certificate_info_get_issuer_common_name +poppler_certificate_info_get_issuer_email +poppler_certificate_info_get_issuer_organization +poppler_certificate_info_get_subject_common_name +poppler_certificate_info_get_subject_email +poppler_certificate_info_get_subject_organization poppler_form_field_button_get_button_type poppler_form_field_button_get_state poppler_form_field_button_set_state @@ -413,10 +423,62 @@ poppler_form_field_text_is_rich_text poppler_form_field_text_set_text poppler_signature_info_copy poppler_signature_info_free +poppler_signature_info_get_certificate_info poppler_signature_info_get_certificate_status poppler_signature_info_get_signature_status poppler_signature_info_get_signer_name poppler_signature_info_get_local_signing_time +poppler_signing_data_new +poppler_signing_data_copy +poppler_signing_data_free +poppler_signing_data_set_destination_filename +poppler_signing_data_set_certificate_info +poppler_signing_data_set_page +poppler_signing_data_set_signature_text +poppler_signing_data_set_signature_text_left +poppler_signing_data_set_signature_rectangle +poppler_signing_data_set_font_color +poppler_signing_data_set_font_size +poppler_signing_data_set_left_font_size +poppler_signing_data_set_border_color +poppler_signing_data_set_border_width +poppler_signing_data_set_background_color +poppler_signing_data_set_field_partial_name +poppler_signing_data_set_reason +poppler_signing_data_set_location +poppler_signing_data_set_image_path +poppler_signing_data_set_password +poppler_signing_data_set_document_owner_password +poppler_signing_data_set_document_user_password +poppler_signing_data_get_destination_filename +poppler_signing_data_get_certificate_info +poppler_signing_data_get_page +poppler_signing_data_get_signature_text +poppler_signing_data_get_signature_text_left +poppler_signing_data_get_signature_rectangle +poppler_signing_data_get_font_color +poppler_signing_data_get_font_size +poppler_signing_data_get_left_font_size +poppler_signing_data_get_border_color +poppler_signing_data_get_border_width +poppler_signing_data_get_background_color +poppler_signing_data_get_field_partial_name +poppler_signing_data_get_reason +poppler_signing_data_get_location +poppler_signing_data_get_image_path +poppler_signing_data_get_password +poppler_signing_data_get_document_owner_password +poppler_signing_data_get_document_user_password +poppler_certificate_info_new +poppler_certificate_info_copy +poppler_certificate_info_get_nick_name +poppler_certificate_info_get_subject_common_name +poppler_get_certificate_info_by_nick_name +poppler_set_nss_dir +poppler_get_nss_dir +poppler_set_nss_password_callback +poppler_get_available_signing_certificates +poppler_certificate_info_free POPPLER_FORM_FIELD @@ -465,6 +527,7 @@ PopplerAnnotMarkupReplyType PopplerAnnotTextState PopplerAnnotStampIcon PopplerAnnotType +PopplerFontDescription poppler_annot_get_annot_type POPPLER_ANNOT_TEXT_ICON_CIRCLE POPPLER_ANNOT_TEXT_ICON_COMMENT @@ -484,7 +547,12 @@ poppler_annot_circle_set_interior_color poppler_annot_file_attachment_get_attachment poppler_annot_file_attachment_get_name poppler_annot_free_text_get_callout_line +poppler_annot_free_text_get_font_color +poppler_annot_free_text_get_font_desc poppler_annot_free_text_get_quadding +poppler_annot_free_text_new +poppler_annot_free_text_set_font_desc +poppler_annot_free_text_set_font_color poppler_annot_get_color poppler_annot_get_contents poppler_annot_get_flags @@ -492,6 +560,7 @@ poppler_annot_get_modified poppler_annot_get_name poppler_annot_get_page_index poppler_annot_get_rectangle +poppler_annot_get_border_width poppler_annot_line_new poppler_annot_line_set_vertices poppler_annot_markup_get_date @@ -515,6 +584,7 @@ poppler_annot_set_color poppler_annot_set_contents poppler_annot_set_flags poppler_annot_set_rectangle +poppler_annot_set_border_width poppler_annot_square_get_interior_color poppler_annot_square_new poppler_annot_square_set_interior_color @@ -534,6 +604,30 @@ poppler_annot_text_markup_set_quadrilaterals poppler_annot_text_new poppler_annot_text_set_icon poppler_annot_text_set_is_open +poppler_font_description_copy +poppler_font_description_free +poppler_font_description_new +POPPLER_STRETCH_ULTRA_CONDENSED +POPPLER_STRETCH_EXTRA_CONDENSED +POPPLER_STRETCH_CONDENSED +POPPLER_STRETCH_SEMI_CONDENSED +POPPLER_STRETCH_NORMAL +POPPLER_STRETCH_SEMI_EXPANDED +POPPLER_STRETCH_EXPANDED +POPPLER_STRETCH_EXTRA_EXPANDED +POPPLER_STRETCH_ULTRA_EXPANDED +POPPLER_STYLE_NORMAL +POPPLER_STYLE_OBLIQUE +POPPLER_STYLE_ITALIC +POPPLER_WEIGHT_THIN +POPPLER_WEIGHT_ULTRALIGHT +POPPLER_WEIGHT_LIGHT +POPPLER_WEIGHT_NORMAL +POPPLER_WEIGHT_MEDIUM +POPPLER_WEIGHT_SEMIBOLD +POPPLER_WEIGHT_BOLD +POPPLER_WEIGHT_ULTRABOLD +POPPLER_WEIGHT_HEAVY POPPLER_ANNOT @@ -598,6 +692,7 @@ poppler_annot_text_get_type poppler_annot_text_markup_get_type poppler_annot_text_state_get_type poppler_annot_type_get_type +poppler_font_description_get_type
    diff --git a/glib/reference/poppler.types b/glib/reference/poppler.types index 1ab636b..c97ad17 100644 --- a/glib/reference/poppler.types +++ b/glib/reference/poppler.types @@ -25,6 +25,7 @@ poppler_annot_text_state_get_type poppler_annot_type_get_type poppler_attachment_get_type poppler_backend_get_type +poppler_certificate_info_get_type poppler_certificate_status_get_type poppler_color_get_type poppler_dest_get_type @@ -92,3 +93,6 @@ poppler_structure_writing_mode_get_type poppler_text_attributes_get_type poppler_text_span_get_type poppler_viewer_preferences_get_type +poppler_signing_data_get_type +poppler_certificate_info_get_type +poppler_font_description_get_type \ No newline at end of file diff --git a/glib/tests/CMakeLists.txt b/glib/tests/CMakeLists.txt index 19ebf57..a09c66d 100644 --- a/glib/tests/CMakeLists.txt +++ b/glib/tests/CMakeLists.txt @@ -1,7 +1,3 @@ -macro(POPPLER_ADD_TESTCASE exe arg1) - add_test(${exe}-${arg1} ${EXE} ${EXECUTABLE_OUTPUT_PATH}/poppler-check-bb ${TESTDATADIR}/unittestcases/${arg1} ${ARGN}) -endmacro(POPPLER_ADD_TESTCASE) - add_definitions(-DTESTDATADIR=\"${TESTDATADIR}\") set(poppler_check_text_SRCS @@ -18,27 +14,31 @@ poppler_add_test(poppler-check-bb BUILD_GTK_TESTS ${poppler_check_bb_SRCS}) target_link_libraries(poppler-check-text poppler-glib PkgConfig::GTK3) target_link_libraries(poppler-check-bb poppler-glib PkgConfig::GTK3) -poppler_add_testcase(poppler-check-bb shapes+attachments.pdf 42.5 42.5 557.5 557.5) -poppler_add_testcase(poppler-check-bb orientation.pdf 34 34 83.74 49 793 34 808 97.19 488.02 793 561 808 34 503.61 49 561) -poppler_add_testcase(poppler-check-bb xr01.pdf 148.71 126.35 308.11 704.57) -poppler_add_testcase(poppler-check-bb xr02.pdf 133.77 124.81 308.11 704.57 133.77 124.80 308.11 704.57) -poppler_add_testcase(poppler-check-bb russian.pdf 71.5 76.81 197.69 131.09) -poppler_add_testcase(poppler-check-bb vis_policy_test.pdf 90 77.93 312.01 265.13) -poppler_add_testcase(poppler-check-bb searchAcrossLines.pdf 107.15 105.23 523.85 691 85.04 94 538.59 762.19) -poppler_add_testcase(poppler-check-bb deseret.pdf 56.8 57.15 109.5 72.8) -poppler_add_testcase(poppler-check-bb fieldWithUtf16Names.pdf 56.65 56.65 264.55 83.05) -poppler_add_testcase(poppler-check-bb bug7063.pdf 56.8 57.46 244.29 118.79) -poppler_add_testcase(poppler-check-bb WithActualText.pdf 100 90.72 331.01 102.35) -poppler_add_testcase(poppler-check-bb Issue637.pdf 70.87 53 293 105.37) -poppler_add_testcase(poppler-check-bb truetype.pdf 17.5 17.5 577.5 225.62) -poppler_add_testcase(poppler-check-bb form_set_icon.pdf 0 0 362.835 272.126) -poppler_add_testcase(poppler-check-bb imageretrieve+attachment.pdf 0 0 610.56 792) -poppler_add_testcase(poppler-check-bb checkbox_issue_159.pdf 2.84 14.17 553.18 840.87) -poppler_add_testcase(poppler-check-bb NestedLayers.pdf 0 191 612 792) -poppler_add_testcase(poppler-check-bb A6EmbeddedFiles.pdf 18 18 558.36 751.92) -poppler_add_testcase(poppler-check-bb latex-hyperref-checkbox-issue-655.pdf 148.71 123.81 308.11 704.57) -poppler_add_testcase(poppler-check-bb utf16le-annot.pdf 55.47 54.78 98.74 96.12) -poppler_add_testcase(poppler-check-bb type3.pdf -p 10 125.80 130 509.30 695 125.80 132 538.03 693) +macro(GLIB_ADD_BBTEST arg1) + add_test(poppler-check-bb-${arg1} ${EXE} ${EXECUTABLE_OUTPUT_PATH}/poppler-check-bb ${TESTDATADIR}/unittestcases/${arg1} ${ARGN}) +endmacro(GLIB_ADD_BBTEST) + +glib_add_bbtest(shapes+attachments.pdf 42.5 42.5 557.5 557.5) +glib_add_bbtest(orientation.pdf 34 34 83.74 49 793 34 808 97.19 488.02 793 561 808 34 503.61 49 561) +glib_add_bbtest(xr01.pdf 148.71 126.35 308.11 704.57) +glib_add_bbtest(xr02.pdf 133.77 124.81 308.11 704.57 133.77 124.80 308.11 704.57) +glib_add_bbtest(russian.pdf 71.5 76.81 197.69 131.09) +glib_add_bbtest(vis_policy_test.pdf 90 77.93 312.01 265.13) +glib_add_bbtest(searchAcrossLines.pdf 107.15 105.23 523.85 691 85.04 94 538.59 762.19) +glib_add_bbtest(deseret.pdf 56.8 57.15 109.5 72.8) +glib_add_bbtest(fieldWithUtf16Names.pdf 56.65 56.65 264.55 83.05) +glib_add_bbtest(bug7063.pdf 56.8 57.46 244.29 118.79) +glib_add_bbtest(WithActualText.pdf 100 90.72 331.01 102.35) +glib_add_bbtest(Issue637.pdf 70.87 53 293 105.37) +glib_add_bbtest(truetype.pdf 17.5 17.5 577.5 225.62) +glib_add_bbtest(form_set_icon.pdf 0 0 362.835 272.126) +glib_add_bbtest(imageretrieve+attachment.pdf 0 0 610.56 792) +glib_add_bbtest(checkbox_issue_159.pdf 2.84 14.17 553.18 840.87) +glib_add_bbtest(NestedLayers.pdf 0 191 612 792) +glib_add_bbtest(A6EmbeddedFiles.pdf 18 18 558.36 751.92) +glib_add_bbtest(latex-hyperref-checkbox-issue-655.pdf 148.71 123.81 308.11 704.57) +glib_add_bbtest(utf16le-annot.pdf 55.47 54.78 98.74 96.12) +glib_add_bbtest(type3.pdf -p 10 125.80 130 509.30 695 125.80 132 538.03 693) add_executable(pdfdrawbb pdfdrawbb.c) target_link_libraries(pdfdrawbb poppler-glib) diff --git a/glib/tests/check_bb.c b/glib/tests/check_bb.c index 7a4805f..d5f85dd 100644 --- a/glib/tests/check_bb.c +++ b/glib/tests/check_bb.c @@ -22,7 +22,7 @@ int equal(double a, double b, double precision) */ int main(int argc, char *argv[]) { - GFile *infile; + g_autoptr(GFile) infile = NULL; PopplerDocument *doc; PopplerPage *page; int npages, n; @@ -93,5 +93,7 @@ int main(int argc, char *argv[]) g_object_unref(page); } + g_object_unref(doc); + return EXIT_SUCCESS; } diff --git a/glib/tests/fuzzing/doc_attr_fuzzer.cc b/glib/tests/fuzzing/doc_attr_fuzzer.cc index 18d3811..011b744 100644 --- a/glib/tests/fuzzing/doc_attr_fuzzer.cc +++ b/glib/tests/fuzzing/doc_attr_fuzzer.cc @@ -29,5 +29,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) poppler_document_set_title(doc, buf); free(buf); + g_object_unref(doc); return 0; } diff --git a/glib/tests/fuzzing/find_text_fuzzer.cc b/glib/tests/fuzzing/find_text_fuzzer.cc index e7d0ba9..4bab80f 100644 --- a/glib/tests/fuzzing/find_text_fuzzer.cc +++ b/glib/tests/fuzzing/find_text_fuzzer.cc @@ -10,6 +10,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) PopplerPage *page; char *buf; int npages; + GList *matches; doc = poppler_document_new_from_data((char *)data, size, NULL, &err); if (doc == NULL) { @@ -19,6 +20,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) npages = poppler_document_get_n_pages(doc); if (npages < 1) { + g_object_unref(doc); return 0; } @@ -31,9 +33,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (!page) { continue; } - poppler_page_find_text(page, buf); + if (g_utf8_validate(buf, -1, NULL)) { + matches = poppler_page_find_text(page, buf); + if (matches) { + g_list_free_full(matches, (GDestroyNotify)poppler_rectangle_free); + } + } g_object_unref(page); } free(buf); + g_object_unref(doc); return 0; } diff --git a/glib/tests/fuzzing/label_fuzzer.cc b/glib/tests/fuzzing/label_fuzzer.cc index 825a57a..2e831d3 100644 --- a/glib/tests/fuzzing/label_fuzzer.cc +++ b/glib/tests/fuzzing/label_fuzzer.cc @@ -19,6 +19,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) npages = poppler_document_get_n_pages(doc); if (npages < 1) { + g_object_unref(doc); return 0; } @@ -34,5 +35,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) g_object_unref(page); } free(buf); + g_object_unref(doc); return 0; } diff --git a/goo/GooString.cc b/goo/GooString.cc index 010058f..dde3536 100644 --- a/goo/GooString.cc +++ b/goo/GooString.cc @@ -29,7 +29,8 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018 Greg Knight -// Copyright (C) 2019, 2022 Oliver Sander +// Copyright (C) 2019, 2022-2024 Oliver Sander +// Copyright (C) 2023 Even Rouault // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -38,14 +39,17 @@ #include +#include #include #include #include #include #include #include +#include #include "gmem.h" +#include "Error.h" #include "GooString.h" //------------------------------------------------------------------------ @@ -121,25 +125,25 @@ void formatDoubleSmallAware(double x, char *buf, int bufSize, int prec, bool tri //------------------------------------------------------------------------ -std::unique_ptr GooString::format(const char *fmt, ...) +std::string GooString::format(const char *fmt, ...) { - auto s = std::make_unique(); + GooString s; va_list argList; va_start(argList, fmt); - s->appendfv(fmt, argList); + s.appendfv(fmt, argList); va_end(argList); - return s; + return s.toStr(); } -std::unique_ptr GooString::formatv(const char *fmt, va_list argList) +std::string GooString::formatv(const char *fmt, va_list argList) { - auto s = std::make_unique(); + GooString s; - s->appendfv(fmt, argList); + s.appendfv(fmt, argList); - return s; + return s.toStr(); } GooString *GooString::appendf(const char *fmt, ...) @@ -412,11 +416,18 @@ GooString *GooString::appendfv(const char *fmt, va_list argList) len = 1; reverseAlign = !reverseAlign; break; - case fmtString: + case fmtString: { str = arg.s; - len = strlen(str); + const size_t strlen_str = strlen(str); + if (strlen_str > static_cast(std::numeric_limits::max())) { + error(errSyntaxWarning, 0, "String truncated to INT_MAX bytes"); + len = std::numeric_limits::max(); + } else { + len = static_cast(strlen_str); + } reverseAlign = !reverseAlign; break; + } case fmtGooString: if (arg.gs) { str = arg.gs->c_str(); @@ -609,35 +620,3 @@ std::string GooString::toLowerCase(const std::string &s) lowerCase(newString); return s; } - -void GooString::prependUnicodeMarker() -{ - insert(0, "\xFE\xFF", 2); -} - -bool GooString::startsWith(const char *prefix) const -{ - return startsWith(toStr(), prefix); -} - -bool GooString::endsWith(const char *suffix) const -{ - return endsWith(toStr(), suffix); -} - -GooString *GooString::sanitizedName() const -{ - auto *name = new GooString(); - - for (const auto c : *this) { - if (c <= (char)0x20 || c >= (char)0x7f || c == ' ' || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%' || c == '#') { - char buf[8]; - sprintf(buf, "#%02x", c & 0xff); - name->append(buf); - } else { - name->append(c); - } - } - - return name; -} diff --git a/goo/GooString.h b/goo/GooString.h index 5af12d0..86a442a 100644 --- a/goo/GooString.h +++ b/goo/GooString.h @@ -17,7 +17,7 @@ // // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2006 Krzysztof Kowalczyk -// Copyright (C) 2008-2010, 2012, 2014, 2017-2022 Albert Astals Cid +// Copyright (C) 2008-2010, 2012, 2014, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2012-2014 Fabio D'Urso // Copyright (C) 2013 Jason Crain // Copyright (C) 2015, 2018 Adam Reichold @@ -26,7 +26,7 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2019 Christophe Fergeau // Copyright (C) 2019 Tomoyuki Kubota -// Copyright (C) 2019, 2020, 2022 Oliver Sander +// Copyright (C) 2019, 2020, 2022-2024 Oliver Sander // Copyright (C) 2019 Hans-Ulrich Jüttner // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2022 Even Rouault @@ -103,7 +103,7 @@ class GooString : private std::string // Copy a string. explicit GooString(const GooString *str) : std::string(str ? static_cast(*str) : std::string {}) { } - GooString *copy() const { return new GooString(this); } + std::unique_ptr copy() const { return std::make_unique(this); } // Concatenate two strings. GooString(const GooString *str1, const GooString *str2) @@ -140,8 +140,8 @@ class GooString : private std::string // t -- GooString * // w -- blank space; arg determines width // To get literal curly braces, use {{ or }}. - POPPLER_PRIVATE_EXPORT static std::unique_ptr format(const char *fmt, ...) GOOSTRING_FORMAT; - POPPLER_PRIVATE_EXPORT static std::unique_ptr formatv(const char *fmt, va_list argList); + POPPLER_PRIVATE_EXPORT static std::string format(const char *fmt, ...) GOOSTRING_FORMAT; + POPPLER_PRIVATE_EXPORT static std::string formatv(const char *fmt, va_list argList); // Get length. int getLength() const { return size(); } @@ -156,11 +156,7 @@ class GooString : private std::string void setChar(int i, char c) { (*this)[i] = c; } // Clear string to zero length. - GooString *clear() - { - static_cast(*this).clear(); - return this; - } + using std::string::clear; // Append a character or string. GooString *append(char c) @@ -242,25 +238,10 @@ class GooString : private std::string int cmpN(const char *sA, int n) const { return compare(0, n, sA); } // Return true if strings starts with prefix - POPPLER_PRIVATE_EXPORT bool startsWith(const char *prefix) const; - // Return true if string ends with suffix - POPPLER_PRIVATE_EXPORT bool endsWith(const char *suffix) const; + using std::string::starts_with; - static bool startsWith(std::string_view str, std::string_view prefix) { return str.size() >= prefix.size() && 0 == str.compare(0, prefix.size(), prefix); } - static bool endsWith(std::string_view str, std::string_view suffix) { return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); } - - bool hasUnicodeMarker() const { return hasUnicodeMarker(*this); } - static bool hasUnicodeMarker(const std::string &s) { return s.size() >= 2 && s[0] == '\xfe' && s[1] == '\xff'; } - bool hasUnicodeMarkerLE() const { return hasUnicodeMarkerLE(*this); } - static bool hasUnicodeMarkerLE(const std::string &s) { return s.size() >= 2 && s[0] == '\xff' && s[1] == '\xfe'; } - bool hasJustUnicodeMarker() const { return size() == 2 && hasUnicodeMarker(); } - - POPPLER_PRIVATE_EXPORT void prependUnicodeMarker(); - - // Sanitizes the string so that it does - // not contain any ( ) < > [ ] { } / % - // The caller owns the return value - POPPLER_PRIVATE_EXPORT GooString *sanitizedName() const; + // Return true if string ends with suffix + using std::string::ends_with; }; #endif diff --git a/goo/ImgWriter.cc b/goo/ImgWriter.cc index 071fc7c..5d9e3c0 100644 --- a/goo/ImgWriter.cc +++ b/goo/ImgWriter.cc @@ -10,4 +10,4 @@ #include "ImgWriter.h" -ImgWriter::~ImgWriter() { } +ImgWriter::~ImgWriter() = default; diff --git a/goo/JpegWriter.cc b/goo/JpegWriter.cc index ca69fd0..6451dee 100644 --- a/goo/JpegWriter.cc +++ b/goo/JpegWriter.cc @@ -13,6 +13,7 @@ // Copyright (C) 2018 Martin Packman // Copyright (C) 2018 Ed Porras // Copyright (C) 2021 Peter Williams +// Copyright (C) 2023 Jordan Abrahams-Whitehead // //======================================================================== @@ -23,7 +24,7 @@ #ifdef ENABLE_LIBJPEG # include "poppler/Error.h" - +# include extern "C" { # include } diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc index c3449ae..58d5560 100644 --- a/goo/PNGWriter.cc +++ b/goo/PNGWriter.cc @@ -6,7 +6,7 @@ // // Copyright (C) 2009 Warren Toomey // Copyright (C) 2009 Shen Liang -// Copyright (C) 2009, 2011, 2022 Albert Astals Cid +// Copyright (C) 2009, 2011-2023 Albert Astals Cid // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2011, 2013, 2017 Adrian Johnson // Copyright (C) 2011 Thomas Klausner @@ -29,23 +29,23 @@ struct PNGWriterPrivate { + explicit PNGWriterPrivate(PNGWriter::Format f) : format(f) { } + PNGWriter::Format format; - png_structp png_ptr; - png_infop info_ptr; - unsigned char *icc_data; - int icc_data_size; - char *icc_name; - bool sRGB_profile; + png_structp png_ptr = nullptr; + png_infop info_ptr = nullptr; + unsigned char *icc_data = nullptr; + int icc_data_size = 0; + char *icc_name = nullptr; + bool sRGB_profile = false; + + PNGWriterPrivate(const PNGWriterPrivate &) = delete; + PNGWriterPrivate &operator=(const PNGWriterPrivate &) = delete; }; PNGWriter::PNGWriter(Format formatA) { - priv = new PNGWriterPrivate; - priv->format = formatA; - priv->icc_data = nullptr; - priv->icc_data_size = 0; - priv->icc_name = nullptr; - priv->sRGB_profile = false; + priv = new PNGWriterPrivate(formatA); } PNGWriter::~PNGWriter() diff --git a/goo/gbase64.cc b/goo/gbase64.cc index 2cbc8ae..3ef27c4 100644 --- a/goo/gbase64.cc +++ b/goo/gbase64.cc @@ -20,7 +20,7 @@ static void b64encodeTriplet(char output[4], unsigned char a, unsigned char b, u output[0] = base64table[((a >> 2) & 0x3f)]; // upper 6 of first byte output[1] = base64table[((a << 4) & 0x30) | ((b >> 4) & 0x0f)]; // lower 2 of first byte, upper 4 of second byte output[2] = base64table[((b << 2) & 0x3c) | ((c >> 6) & 0x03)]; // lower 4 of second byte, upper 2 of third byte - output[3] = base64table[((c)&0x3f)]; // lower 6 of third byte + output[3] = base64table[((c) & 0x3f)]; // lower 6 of third byte } std::string gbase64Encode(const void *input, size_t len) diff --git a/goo/gdir.h b/goo/gdir.h deleted file mode 100644 index 7606879..0000000 --- a/goo/gdir.h +++ /dev/null @@ -1,96 +0,0 @@ -//======================================================================== -// -// gfile.h -// -// Miscellaneous file and directory name manipulation. -// -// Copyright 1996-2003 Glyph & Cog, LLC -// -//======================================================================== - -//======================================================================== -// -// Modified under the Poppler project - http://poppler.freedesktop.org -// -// All changes made under the Poppler project to this file are licensed -// under GPL version 2 or later -// -// Copyright (C) 2006 Kristian Høgsberg -// Copyright (C) 2009, 2011, 2012, 2017, 2018, 2021, 2022 Albert Astals Cid -// Copyright (C) 2009 Kovid Goyal -// Copyright (C) 2013 Adam Reichold -// Copyright (C) 2013, 2017 Adrian Johnson -// Copyright (C) 2014 Bogdan Cristea -// Copyright (C) 2014 Peter Breitenlohner -// Copyright (C) 2017 Christoph Cullmann -// Copyright (C) 2017 Thomas Freitag -// Copyright (C) 2018 Mojca Miklavec -// Copyright (C) 2019 Christian Persch -// -// To see a description of the changes please see the Changelog file that -// came with your tarball or type make ChangeLog if you are building from git -// -//======================================================================== - -#ifndef GDIR_H -#define GDIR_H - -#include "poppler-config.h" - -#include - -class GooString; - -#if defined(_WIN32) -# include -#else -# include -#endif - -//------------------------------------------------------------------------ -// GDir and GDirEntry -//------------------------------------------------------------------------ - -class GDirEntry -{ -public: - GDirEntry(const char *dirPath, const char *nameA, bool doStat); - ~GDirEntry(); - - GDirEntry(const GDirEntry &other) = delete; - GDirEntry &operator=(const GDirEntry &other) = delete; - - const GooString *getName() const { return name; } - const GooString *getFullPath() const { return fullPath; } - bool isDir() const { return dir; } - -private: - GooString *name; // dir/file name - GooString *fullPath; - bool dir; // is it a directory? -}; - -class GDir -{ -public: - explicit GDir(const char *name, bool doStatA = true); - ~GDir(); - - GDir(const GDir &other) = delete; - GDir &operator=(const GDir &other) = delete; - - std::unique_ptr getNextEntry(); - void rewind(); - -private: - GooString *path; // directory path - bool doStat; // call stat() for each entry? -#if defined(_WIN32) - WIN32_FIND_DATAA ffd; - HANDLE hnd; -#else - DIR *dir; // the DIR structure from opendir() -#endif -}; - -#endif diff --git a/goo/gfile.cc b/goo/gfile.cc index 5ae4ffc..499550a 100644 --- a/goo/gfile.cc +++ b/goo/gfile.cc @@ -19,7 +19,7 @@ // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2008 Adam Batkin // Copyright (C) 2008, 2010, 2012, 2013 Hib Eris -// Copyright (C) 2009, 2012, 2014, 2017, 2018, 2021, 2022 Albert Astals Cid +// Copyright (C) 2009, 2012, 2014, 2017, 2018, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013, 2017 Adrian Johnson @@ -48,7 +48,6 @@ #include #include "GooString.h" #include "gfile.h" -#include "gdir.h" // Some systems don't define this, so just make it something reasonably // large. @@ -434,112 +433,3 @@ bool GooFile::modificationTimeChangedSinceOpen() const } #endif // _WIN32 - -//------------------------------------------------------------------------ -// GDir and GDirEntry -//------------------------------------------------------------------------ - -GDirEntry::GDirEntry(const char *dirPath, const char *nameA, bool doStat) -{ -#ifdef _WIN32 - DWORD fa; -#else - struct stat st; -#endif - - name = new GooString(nameA); - dir = false; - fullPath = new GooString(dirPath); - appendToPath(fullPath, nameA); - if (doStat) { -#ifdef _WIN32 - fa = GetFileAttributesA(fullPath->c_str()); - dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY)); -#else - if (stat(fullPath->c_str(), &st) == 0) { - dir = S_ISDIR(st.st_mode); - } -#endif - } -} - -GDirEntry::~GDirEntry() -{ - delete fullPath; - delete name; -} - -GDir::GDir(const char *name, bool doStatA) -{ - path = new GooString(name); - doStat = doStatA; -#ifdef _WIN32 - GooString *tmp; - - tmp = path->copy(); - tmp->append("/*.*"); - hnd = FindFirstFileA(tmp->c_str(), &ffd); - delete tmp; -#else - dir = opendir(name); -#endif -} - -GDir::~GDir() -{ - delete path; -#ifdef _WIN32 - if (hnd != INVALID_HANDLE_VALUE) { - FindClose(hnd); - hnd = INVALID_HANDLE_VALUE; - } -#else - if (dir) { - closedir(dir); - } -#endif -} - -std::unique_ptr GDir::getNextEntry() -{ -#ifdef _WIN32 - if (hnd != INVALID_HANDLE_VALUE) { - auto e = std::make_unique(path->c_str(), ffd.cFileName, doStat); - if (!FindNextFileA(hnd, &ffd)) { - FindClose(hnd); - hnd = INVALID_HANDLE_VALUE; - } - return e; - } -#else - struct dirent *ent; - if (dir) { - do { - ent = readdir(dir); - } while (ent && (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))); - if (ent) { - return std::make_unique(path->c_str(), ent->d_name, doStat); - } - } -#endif - - return {}; -} - -void GDir::rewind() -{ -#ifdef _WIN32 - GooString *tmp; - - if (hnd != INVALID_HANDLE_VALUE) - FindClose(hnd); - tmp = path->copy(); - tmp->append("/*.*"); - hnd = FindFirstFileA(tmp->c_str(), &ffd); - delete tmp; -#else - if (dir) { - rewinddir(dir); - } -#endif -} diff --git a/goo/gfile.h b/goo/gfile.h index 08b1b5d..516c2fc 100644 --- a/goo/gfile.h +++ b/goo/gfile.h @@ -132,10 +132,7 @@ class POPPLER_PRIVATE_EXPORT GooFile #ifdef _WIN32 static std::unique_ptr open(const wchar_t *fileName); - ~GooFile() - { - CloseHandle(handle); - } + ~GooFile() { CloseHandle(handle); } // Asuming than on windows you can't change files that are already open bool modificationTimeChangedSinceOpen() const; @@ -145,10 +142,7 @@ class POPPLER_PRIVATE_EXPORT GooFile HANDLE handle; struct _FILETIME modifiedTimeOnOpen; #else - ~GooFile() - { - close(fd); - } + ~GooFile() { close(fd); } bool modificationTimeChangedSinceOpen() const; diff --git a/goo/gstrtod.cc b/goo/gstrtod.cc index cf57327..4cca4fa 100644 --- a/goo/gstrtod.cc +++ b/goo/gstrtod.cc @@ -27,8 +27,8 @@ #include #include -#define ascii_isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') -#define ascii_isdigit(c) (c >= '0' && c <= '9') +#define ascii_isspace(c) ((c) == ' ' || (c) == '\f' || (c) == '\n' || (c) == '\r' || (c) == '\t' || (c) == '\v') +#define ascii_isdigit(c) ((c) >= '0' && (c) <= '9') double gatof(const char *nptr) { diff --git a/poppler/Annot.cc b/poppler/Annot.cc index 5716ea5..35a4e1c 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2006 Scott Turner // Copyright (C) 2007, 2008 Julien Rebetez -// Copyright (C) 2007-2013, 2015-2022 Albert Astals Cid +// Copyright (C) 2007-2013, 2015-2024 Albert Astals Cid // Copyright (C) 2007-2013, 2018 Carlos Garcia Campos // Copyright (C) 2007, 2008 Iñigo Martínez // Copyright (C) 2007 Jeff Muizelaar @@ -41,20 +41,24 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018 Dileep Sankhla // Copyright (C) 2018-2020 Tobias Deiminger -// Copyright (C) 2018-2020, 2022 Oliver Sander +// Copyright (C) 2018-2020, 2022, 2024 Oliver Sander // Copyright (C) 2019 Umang Malik // Copyright (C) 2019 João Netto -// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden +// Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright (C) 2020 Katarina Behrens // Copyright (C) 2020 Thorsten Behrens -// Copyright (C) 2020 Nelson Benítez León +// Copyright (C) 2020, 2025 Nelson Benítez León // Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, . // Copyright (C) 2021 Zachary Travis // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2022 Martin // Copyright (C) 2022 Andreas Naumann <42870-ANaumann85@users.noreply.gitlab.freedesktop.org> -// Copyright (C) 2022 Erich E. Hoover +// Copyright (C) 2022, 2024 Erich E. Hoover +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Pratham Gandhi +// Copyright (C) 2024 Carsten Emde +// Copyright (C) 2024 Lucas Baudin // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -66,6 +70,7 @@ #include #include #include +#include #include "goo/gmem.h" #include "goo/gstrtod.h" #include "Error.h" @@ -80,7 +85,6 @@ #include "CharCodeToUnicode.h" #include "PDFDocEncoding.h" #include "Form.h" -#include "Error.h" #include "XRef.h" #include "Movie.h" #include "OptionalContent.h" @@ -88,6 +92,7 @@ #include "FileSpec.h" #include "DateInfo.h" #include "Link.h" +#include "UTF.h" #include #include @@ -340,14 +345,6 @@ double AnnotPath::getY(int coord) const return 0; } -AnnotCoord *AnnotPath::getCoord(int coord) -{ - if (coord >= 0 && coord < getCoordsLength()) { - return &coords[coord]; - } - return nullptr; -} - void AnnotPath::parsePathArray(Array *array) { if (array->getLength() % 2) { @@ -442,7 +439,7 @@ AnnotQuadrilaterals::AnnotQuadrilaterals(std::unique_ptr & AnnotQuadrilaterals::~AnnotQuadrilaterals() = default; -double AnnotQuadrilaterals::getX1(int quadrilateral) +double AnnotQuadrilaterals::getX1(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord1.getX(); @@ -450,7 +447,7 @@ double AnnotQuadrilaterals::getX1(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getY1(int quadrilateral) +double AnnotQuadrilaterals::getY1(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord1.getY(); @@ -458,7 +455,7 @@ double AnnotQuadrilaterals::getY1(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getX2(int quadrilateral) +double AnnotQuadrilaterals::getX2(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord2.getX(); @@ -466,7 +463,7 @@ double AnnotQuadrilaterals::getX2(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getY2(int quadrilateral) +double AnnotQuadrilaterals::getY2(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord2.getY(); @@ -474,7 +471,7 @@ double AnnotQuadrilaterals::getY2(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getX3(int quadrilateral) +double AnnotQuadrilaterals::getX3(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord3.getX(); @@ -482,7 +479,7 @@ double AnnotQuadrilaterals::getX3(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getY3(int quadrilateral) +double AnnotQuadrilaterals::getY3(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord3.getY(); @@ -490,7 +487,7 @@ double AnnotQuadrilaterals::getY3(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getX4(int quadrilateral) +double AnnotQuadrilaterals::getX4(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord4.getX(); @@ -498,7 +495,7 @@ double AnnotQuadrilaterals::getX4(int quadrilateral) return 0; } -double AnnotQuadrilaterals::getY4(int quadrilateral) +double AnnotQuadrilaterals::getY4(int quadrilateral) const { if (quadrilateral >= 0 && quadrilateral < quadrilateralsLength) { return quadrilaterals[quadrilateral].coord4.getY(); @@ -623,7 +620,7 @@ Object AnnotBorderArray::writeToObject(XRef *xref) const borderArray->add(Object(verticalCorner)); borderArray->add(Object(width)); - if (dash.size() > 0) { + if (!dash.empty()) { Array *a = new Array(xref); for (double d : dash) { @@ -640,7 +637,7 @@ Object AnnotBorderArray::writeToObject(XRef *xref) const // AnnotBorderBS //------------------------------------------------------------------------ -AnnotBorderBS::AnnotBorderBS() { } +AnnotBorderBS::AnnotBorderBS() = default; AnnotBorderBS::AnnotBorderBS(Dict *dict) { @@ -712,7 +709,7 @@ Object AnnotBorderBS::writeToObject(XRef *xref) const Dict *dict = new Dict(xref); dict->set("W", Object(width)); dict->set("S", Object(objName, getStyleName())); - if (style == borderDashed && dash.size() > 0) { + if (style == borderDashed && !dash.empty()) { Array *a = new Array(xref); for (double d : dash) { @@ -878,7 +875,7 @@ std::string DefaultAppearance::toAppearanceString() const { AnnotAppearanceBuilder appearBuilder; if (fontColor) { - appearBuilder.setDrawColor(fontColor.get(), true); + appearBuilder.setDrawColor(*fontColor, true); } appearBuilder.setTextFont(fontName, fontPtSize); return appearBuilder.buffer()->toStr(); @@ -953,7 +950,7 @@ AnnotAppearance::AnnotAppearance(PDFDoc *docA, Object *dict) appearDict = dict->copy(); } -AnnotAppearance::~AnnotAppearance() { } +AnnotAppearance::~AnnotAppearance() = default; Object AnnotAppearance::getAppearanceStream(AnnotAppearanceType type, const char *state) { @@ -1180,13 +1177,13 @@ std::unique_ptr AnnotAppearanceCharacs::copy() const res->backColor = std::make_unique(*backColor); } if (normalCaption) { - res->normalCaption = std::unique_ptr(normalCaption->copy()); + res->normalCaption = normalCaption->copy(); } if (rolloverCaption) { - res->rolloverCaption = std::unique_ptr(rolloverCaption->copy()); + res->rolloverCaption = rolloverCaption->copy(); } if (alternateCaption) { - res->alternateCaption = std::unique_ptr(alternateCaption->copy()); + res->alternateCaption = alternateCaption->copy(); } if (iconFit) { res->iconFit = std::make_unique(*iconFit); @@ -1345,7 +1342,7 @@ void Annot::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("Contents"); if (obj1.isString()) { - contents.reset(obj1.getString()->copy()); + contents = obj1.getString()->copy(); } else { contents = std::make_unique(); } @@ -1362,12 +1359,12 @@ void Annot::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("NM"); if (obj1.isString()) { - name.reset(obj1.getString()->copy()); + name = obj1.getString()->copy(); } obj1 = dict->lookup("M"); if (obj1.isString()) { - modified.reset(obj1.getString()->copy()); + modified = obj1.getString()->copy(); } //----- get the flags @@ -1404,6 +1401,11 @@ void Annot::initialize(PDFDoc *docA, Dict *dict) //----- get the annotation appearance if (appearStreams) { appearance = appearStreams->getAppearanceStream(AnnotAppearance::appearNormal, appearState->c_str()); + Object obj = appearance.fetch(doc->getXRef()); + if (obj.isStream() && !obj.getStream()->reset()) { + // appearance stream is invalid as reset() returned false - Issue #1557 + appearance.setToNull(); + } } //----- parse the border style @@ -1439,9 +1441,9 @@ void Annot::getRect(double *x1, double *y1, double *x2, double *y2) const *y2 = rect->y2; } -void Annot::setRect(const PDFRectangle *rectA) +void Annot::setRect(const PDFRectangle &rectA) { - setRect(rectA->x1, rectA->y1, rectA->x2, rectA->y2); + setRect(rectA.x1, rectA.y1, rectA.x2, rectA.y2); } void Annot::setRect(double x1, double y1, double x2, double y2) @@ -1482,7 +1484,7 @@ void Annot::update(const char *key, Object &&value) annotLocker(); /* Set M to current time, unless we are updating M itself */ if (strcmp(key, "M") != 0) { - modified.reset(timeToDateString(nullptr)); + modified = timeToDateString(nullptr); annotObj.dictSet("M", Object(modified->copy())); } @@ -1501,8 +1503,8 @@ void Annot::setContents(std::unique_ptr &&new_content) if (new_content) { contents = std::move(new_content); // append the unicode marker if needed - if (!contents->hasUnicodeMarker()) { - contents->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(contents->toStr())) { + prependUnicodeByteOrderMark(contents->toNonConstStr()); } } else { contents = std::make_unique(); @@ -1524,12 +1526,12 @@ void Annot::setName(GooString *new_name) update("NM", Object(name->copy())); } -void Annot::setModified(GooString *new_modified) +void Annot::setModified(std::unique_ptr new_modified) { annotLocker(); if (new_modified) { - modified = std::make_unique(new_modified); + modified = std::move(new_modified); update("M", Object(modified->copy())); } else { modified.reset(nullptr); @@ -1682,13 +1684,13 @@ void Annot::decRefCnt() } } -Annot::~Annot() { } +Annot::~Annot() = default; -void AnnotAppearanceBuilder::setDrawColor(const AnnotColor *drawColor, bool fill) +void AnnotAppearanceBuilder::setDrawColor(const AnnotColor &drawColor, bool fill) { - const double *values = drawColor->getValues(); + const double *values = drawColor.getValues(); - switch (drawColor->getSpace()) { + switch (drawColor.getSpace()) { case AnnotColor::colorCMYK: appearBuf->appendf("{0:.5f} {1:.5f} {2:.5f} {3:.5f} {4:c}\n", values[0], values[1], values[2], values[3], fill ? 'k' : 'K'); break; @@ -1711,12 +1713,12 @@ void AnnotAppearanceBuilder::setTextFont(const Object &fontName, double fontSize } } -void AnnotAppearanceBuilder::setLineStyleForBorder(const AnnotBorder *border) +void AnnotAppearanceBuilder::setLineStyleForBorder(const AnnotBorder &border) { - switch (border->getStyle()) { + switch (border.getStyle()) { case AnnotBorder::borderDashed: appearBuf->append("["); - for (double dash : border->getDash()) { + for (double dash : border.getDash()) { appearBuf->appendf(" {0:.2f}", dash); } appearBuf->append(" ] 0 d\n"); @@ -1725,7 +1727,7 @@ void AnnotAppearanceBuilder::setLineStyleForBorder(const AnnotBorder *border) appearBuf->append("[] 0 d\n"); break; } - appearBuf->appendf("{0:.2f} w\n", border->getWidth()); + appearBuf->appendf("{0:.2f} w\n", border.getWidth()); } // Draw an (approximate) circle of radius centered at (, ). @@ -1765,7 +1767,7 @@ void AnnotAppearanceBuilder::drawCircleTopLeft(double cx, double cy, double r) { double r2; - r2 = r / sqrt(2.0); + r2 = r / std::numbers::sqrt2; appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx + r2, cy + r2); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - (1 - bezierCircle) * r2, cy + (1 + bezierCircle) * r2, cx - r2, cy + r2); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx - (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx - r2, cy - r2); @@ -1778,7 +1780,7 @@ void AnnotAppearanceBuilder::drawCircleBottomRight(double cx, double cy, double { double r2; - r2 = r / sqrt(2.0); + r2 = r / std::numbers::sqrt2; appearBuf->appendf("{0:.2f} {1:.2f} m\n", cx - r2, cy - r2); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx - (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + (1 - bezierCircle) * r2, cy - (1 + bezierCircle) * r2, cx + r2, cy - r2); appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", cx + (1 + bezierCircle) * r2, cy - (1 - bezierCircle) * r2, cx + (1 + bezierCircle) * r2, cy + (1 - bezierCircle) * r2, cx + r2, cy + r2); @@ -2018,7 +2020,7 @@ bool Annot::isVisible(bool printing) } // check the OC - OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig(); + const OCGs *optContentConfig = doc->getCatalog()->getOptContentConfig(); if (optContentConfig) { if (!optContentConfig->optContentIsVisible(&oc)) { return false; @@ -2053,6 +2055,11 @@ void Annot::draw(Gfx *gfx, bool printing) } void Annot::setNewAppearance(Object &&newAppearance) +{ + setNewAppearance(std::move(newAppearance), false); +} + +void Annot::setNewAppearance(Object &&newAppearance, bool keepAppearState) { if (newAppearance.isNull()) { return; @@ -2060,6 +2067,7 @@ void Annot::setNewAppearance(Object &&newAppearance) annotLocker(); if (newAppearance.getType() == ObjType::objStream) { + Object oldAS = annotObj.dictLookup("AS"); invalidateAppearance(); appearance = std::move(newAppearance); @@ -2068,10 +2076,15 @@ void Annot::setNewAppearance(Object &&newAppearance) Object obj1 = Object(new Dict(doc->getXRef())); obj1.dictAdd("N", Object(updatedAppearanceStream)); update("AP", std::move(obj1)); - update("AS", Object(objName, "N")); - Object updatedAP = annotObj.dictLookup("AP"); appearStreams = std::make_unique(doc, &updatedAP); + + if (keepAppearState && !oldAS.isNull()) { + appearState = std::make_unique(oldAS.getName()); + update("AS", std::move(oldAS)); + } else { + update("AS", Object(objName, "N")); + } } else { appearStreams = std::make_unique(doc, &newAppearance); update("AP", std::move(newAppearance)); @@ -2105,7 +2118,7 @@ AnnotPopup::AnnotPopup(PDFDoc *docA, Object &&dictObject, const Object *obj) : A initialize(docA, annotObj.getDict()); } -AnnotPopup::~AnnotPopup() { } +AnnotPopup::~AnnotPopup() = default; void AnnotPopup::initialize(PDFDoc *docA, Dict *dict) { @@ -2152,7 +2165,7 @@ void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("T"); if (obj1.isString()) { - label.reset(obj1.getString()->copy()); + label = obj1.getString()->copy(); } Object popupObj = dict->lookup("Popup"); @@ -2165,7 +2178,7 @@ void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("CreationDate"); if (obj1.isString()) { - date.reset(obj1.getString()->copy()); + date = obj1.getString()->copy(); } const Object &irtObj = dict->lookupNF("IRT"); @@ -2177,7 +2190,7 @@ void AnnotMarkup::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("Subj"); if (obj1.isString()) { - subject.reset(obj1.getString()->copy()); + subject = obj1.getString()->copy(); } obj1 = dict->lookup("RT"); @@ -2208,8 +2221,8 @@ void AnnotMarkup::setLabel(std::unique_ptr &&new_label) if (new_label) { label = std::move(new_label); // append the unicode marker if needed - if (!label->hasUnicodeMarker()) { - label->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(label->toStr())) { + prependUnicodeByteOrderMark(label->toNonConstStr()); } } else { label = std::make_unique(); @@ -2258,10 +2271,10 @@ void AnnotMarkup::setOpacity(double opacityA) invalidateAppearance(); } -void AnnotMarkup::setDate(GooString *new_date) +void AnnotMarkup::setDate(std::unique_ptr new_date) { if (new_date) { - date = std::make_unique(new_date); + date = std::move(new_date); update("CreationDate", Object(date->copy())); } else { date.reset(nullptr); @@ -2664,7 +2677,7 @@ void AnnotText::draw(Gfx *gfx, bool printing) appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), true); + appearBuilder.setDrawColor(*color, true); } else { appearBuilder.append("1 1 1 rg\n"); } @@ -2811,7 +2824,7 @@ AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(do type = typeFreeText; annotObj.dictSet("Subtype", Object(objName, "FreeText")); - annotObj.dictSet("DA", Object(new GooString())); + annotObj.dictSet("DA", Object(std::make_unique())); initialize(docA, annotObj.getDict()); } @@ -2830,7 +2843,7 @@ void AnnotFreeText::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("DA"); if (obj1.isString()) { - appearanceString.reset(obj1.getString()->copy()); + appearanceString = obj1.getString()->copy(); } else { appearanceString = std::make_unique(); error(errSyntaxWarning, -1, "Bad appearance for annotation"); @@ -2845,7 +2858,7 @@ void AnnotFreeText::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("DS"); if (obj1.isString()) { - styleString.reset(obj1.getString()->copy()); + styleString = obj1.getString()->copy(); } obj1 = dict->lookup("CL"); @@ -2933,8 +2946,8 @@ void AnnotFreeText::setStyleString(GooString *new_string) if (new_string) { styleString = std::make_unique(new_string); // append the unicode marker if needed - if (!styleString->hasUnicodeMarker()) { - styleString->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(styleString->toStr())) { + prependUnicodeByteOrderMark(styleString->toNonConstStr()); } } else { styleString = std::make_unique(); @@ -3000,7 +3013,7 @@ static std::unique_ptr createAnnotDrawFont(XRef *xref, Dict *fontParent Dict *fontDict = new Dict(xref); fontDict->add("BaseFont", Object(objName, fontname)); fontDict->add("Subtype", Object(objName, "Type1")); - if (strcmp(fontname, "ZapfDingbats") && strcmp(fontname, "Symbol")) { + if ((strcmp(fontname, "ZapfDingbats") != 0) && (strcmp(fontname, "Symbol") != 0)) { fontDict->add("Encoding", Object(objName, "WinAnsiEncoding")); } @@ -3026,7 +3039,7 @@ class HorizontalTextLayouter double blockWidth; bool newFontNeeded = false; GooString outputText; - const bool isUnicode = text->hasUnicodeMarker(); + const bool isUnicode = hasUnicodeByteOrderMark(text->toStr()); int charCount; Annot::layoutText(text, &outputText, &i, *font, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, noReencode, !noReencode ? &newFontNeeded : nullptr); @@ -3035,7 +3048,7 @@ class HorizontalTextLayouter *availableWidth -= blockWidth; } - while (newFontNeeded && (!availableWidth || *availableWidth > 0)) { + while (newFontNeeded && (!availableWidth || *availableWidth > 0 || (isUnicode && i == 2) || (!isUnicode && i == 0))) { if (!form) { // There's no fonts to look for, so just skip the characters i += isUnicode ? 2 : 1; @@ -3056,7 +3069,7 @@ class HorizontalTextLayouter // Here we just layout one char, we don't know if the one afterwards can be layouted with the original font GooString auxContents = GooString(text->toStr().substr(i, isUnicode ? 2 : 1)); if (isUnicode) { - auxContents.prependUnicodeMarker(); + prependUnicodeByteOrderMark(auxContents.toNonConstStr()); } int auxI = 0; Annot::layoutText(&auxContents, &outputText, &auxI, *auxFont, &blockWidth, availableWidth ? *availableWidth : 0.0, &charCount, false, &newFontNeeded); @@ -3066,7 +3079,9 @@ class HorizontalTextLayouter } // layoutText will always at least layout one character even if it doesn't fit in // the given space which makes sense (except in the case of switching fonts, so we control if we ran out of space here manually) - if (!availableWidth || *availableWidth > 0) { + // we also need to allow the character if we have not layouted anything yet because otherwise we will end up in an infinite loop + // because it is assumed we at least layout one character + if (!availableWidth || *availableWidth > 0 || (isUnicode && i == 2) || (!isUnicode && i == 0)) { i += isUnicode ? 2 : 1; data.emplace_back(outputText.toStr(), auxFontName, blockWidth, charCount); } @@ -3129,6 +3144,36 @@ class HorizontalTextLayouter int consumedText; }; +double Annot::calculateFontSize(const Form *form, const GfxFont *font, const GooString *text, double wMax, double hMax, const bool forceZapfDingbats) +{ + const bool isUnicode = hasUnicodeByteOrderMark(text->toStr()); + double fontSize; + + for (fontSize = 20; fontSize > 1; --fontSize) { + const double availableWidthInFontSize = wMax / fontSize; + double y = hMax - 3; + int i = 0; + while (i < text->getLength()) { + GooString lineText(text->toStr().substr(i)); + if (!hasUnicodeByteOrderMark(lineText.toStr()) && isUnicode) { + prependUnicodeByteOrderMark(lineText.toNonConstStr()); + } + const HorizontalTextLayouter textLayouter(&lineText, form, font, availableWidthInFontSize, forceZapfDingbats); + y -= fontSize; + if (i == 0) { + i += textLayouter.consumedText; + } else { + i += textLayouter.consumedText - (isUnicode ? 2 : 0); + } + } + // approximate the descender for the last line + if (y >= 0.33 * fontSize) { + break; + } + } + return fontSize; +} + struct DrawMultiLineTextResult { std::string text; @@ -3146,8 +3191,8 @@ static DrawMultiLineTextResult drawMultiLineText(const GooString &text, double a const double availableTextWidthInFontPtSize = availableWidth / fontSize; while (i < text.getLength()) { GooString lineText(text.toStr().substr(i)); - if (!lineText.hasUnicodeMarker() && text.hasUnicodeMarker()) { - lineText.prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(lineText.toStr()) && hasUnicodeByteOrderMark(text.toStr())) { + prependUnicodeByteOrderMark(lineText.toNonConstStr()); } const HorizontalTextLayouter textLayouter(&lineText, form, &font, availableTextWidthInFontPtSize, false); @@ -3200,7 +3245,7 @@ static DrawMultiLineTextResult drawMultiLineText(const GooString &text, double a if (i == 0) { i += textLayouter.consumedText; } else { - i += textLayouter.consumedText - (text.hasUnicodeMarker() ? 2 : 0); + i += textLayouter.consumedText - (hasUnicodeByteOrderMark(text.toStr()) ? 2 : 0); } } return result; @@ -3215,7 +3260,7 @@ void AnnotFreeText::generateFreeTextAppearance() borderWidth = border->getWidth(); if (borderWidth > 0) { - appearBuilder.setLineStyleForBorder(border.get()); + appearBuilder.setLineStyleForBorder(*border); } // Box size @@ -3244,11 +3289,11 @@ void AnnotFreeText::generateFreeTextAppearance() bool doStroke = (borderWidth != 0); if (doFill || doStroke) { if (doStroke) { - appearBuilder.setDrawColor(da.getFontColor(), false); // Border color: same as font color + appearBuilder.setDrawColor(*da.getFontColor(), false); // Border color: same as font color } appearBuilder.appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re\n", borderWidth / 2, width - borderWidth, height - borderWidth); if (doFill) { - appearBuilder.setDrawColor(color.get(), true); + appearBuilder.setDrawColor(*color, true); appearBuilder.append(doStroke ? "B\n" : "f\n"); } else { appearBuilder.append("S\n"); @@ -3295,7 +3340,7 @@ void AnnotFreeText::generateFreeTextAppearance() } // Set font state - appearBuilder.setDrawColor(da.getFontColor(), true); + appearBuilder.setDrawColor(*da.getFontColor(), true); appearBuilder.appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin); const DrawMultiLineTextResult textCommands = drawMultiLineText(*contents, textwidth, form, *font, da.getFontName().getName(), da.getFontPtSize(), quadding, 0 /*borderWidth*/); appearBuilder.append(textCommands.text.c_str()); @@ -3580,13 +3625,13 @@ void AnnotLine::generateLineAppearance() AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } if (interiorColor) { - appearBuilder.setDrawColor(interiorColor.get(), true); + appearBuilder.setDrawColor(*interiorColor, true); fill = true; } - appearBuilder.setLineStyleForBorder(border.get()); + appearBuilder.setLineStyleForBorder(*border); borderWidth = border->getWidth(); appearBBox->setBorderWidth(std::max(1., borderWidth)); @@ -3879,19 +3924,19 @@ void AnnotTextMarkup::setType(AnnotSubtype new_type) invalidateAppearance(); } -void AnnotTextMarkup::setQuadrilaterals(AnnotQuadrilaterals *quadPoints) +void AnnotTextMarkup::setQuadrilaterals(const AnnotQuadrilaterals &quadPoints) { Array *a = new Array(doc->getXRef()); - for (int i = 0; i < quadPoints->getQuadrilateralsLength(); ++i) { - a->add(Object(quadPoints->getX1(i))); - a->add(Object(quadPoints->getY1(i))); - a->add(Object(quadPoints->getX2(i))); - a->add(Object(quadPoints->getY2(i))); - a->add(Object(quadPoints->getX3(i))); - a->add(Object(quadPoints->getY3(i))); - a->add(Object(quadPoints->getX4(i))); - a->add(Object(quadPoints->getY4(i))); + for (int i = 0; i < quadPoints.getQuadrilateralsLength(); ++i) { + a->add(Object(quadPoints.getX1(i))); + a->add(Object(quadPoints.getY1(i))); + a->add(Object(quadPoints.getX2(i))); + a->add(Object(quadPoints.getY2(i))); + a->add(Object(quadPoints.getX3(i))); + a->add(Object(quadPoints.getY3(i))); + a->add(Object(quadPoints.getX4(i))); + a->add(Object(quadPoints.getY4(i))); } quadrilaterals = std::make_unique(a, rect.get()); @@ -3956,7 +4001,7 @@ void AnnotTextMarkup::draw(Gfx *gfx, bool printing) switch (type) { case typeUnderline: if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } appearBuilder.append("[] 0 d 1 w\n"); // use a borderwidth, which is consistent with the line width @@ -3977,7 +4022,7 @@ void AnnotTextMarkup::draw(Gfx *gfx, bool printing) break; case typeStrikeOut: if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } blendMultiply = false; appearBuilder.append("[] 0 d 1 w\n"); @@ -4003,7 +4048,7 @@ void AnnotTextMarkup::draw(Gfx *gfx, bool printing) break; case typeSquiggly: if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } appearBuilder.append("[] 0 d 1 w\n"); @@ -4030,7 +4075,7 @@ void AnnotTextMarkup::draw(Gfx *gfx, bool printing) default: case typeHighlight: if (color) { - appearBuilder.setDrawColor(color.get(), true); + appearBuilder.setDrawColor(*color, true); } double biggestBorder = 0; @@ -4182,7 +4227,7 @@ std::unique_ptr AnnotWidget::getFormAdditionalAction(FormAdditionalA return nullptr; } -bool AnnotWidget::setFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType, const GooString &js) +bool AnnotWidget::setFormAdditionalAction(FormAdditionalActionsType formAdditionalActionType, const std::string &js) { Object additionalActionsObject = additionalActions.fetch(doc->getXRef()); @@ -4250,7 +4295,7 @@ void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const G if (!text) { return; } - bool unicode = text->hasUnicodeMarker(); + bool unicode = hasUnicodeByteOrderMark(text->toStr()); bool spacePrev; // previous character was a space // State for backtracking when more text has been processed than fits within @@ -4485,7 +4530,7 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c const GfxFont *font; double fontSize; int tfPos, tmPos; - bool freeText = false; // true if text should be freed before return + std::unique_ptr textToFree; std::unique_ptr fontToFree = nullptr; //~ if there is no MK entry, this should use the existing content stream, @@ -4516,7 +4561,7 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c tok = "/ZaDb"; } } - if (tok.size() >= 1 && tok[0] == '/') { + if (!tok.empty() && tok[0] == '/') { if (!resources || !(font = resources->lookupFont(tok.c_str() + 1).get())) { if (xref != nullptr && resourcesDict != nullptr) { const char *fallback = determineFallbackFont(tok, forceZapfDingbats ? "ZapfDingbats" : "Helvetica"); @@ -4553,18 +4598,17 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c // for a password field, replace all characters with asterisks if (flags & TurnTextToStarsDrawTextFlag) { int len; - if (text->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(text->toStr())) { len = (text->getLength() - 2) / 2; } else { len = text->getLength(); } - GooString *newText = new GooString; + textToFree = std::make_unique(); for (int i = 0; i < len; ++i) { - newText->append('*'); + textToFree->append('*'); } - text = newText; - freeText = true; + text = textToFree.get(); } // setup @@ -4600,32 +4644,10 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c // note: comb is ignored in multiline mode as mentioned in the spec const double wMax = dx - 2 * borderWidth - 4; - const bool isUnicode = text->hasUnicodeMarker(); // compute font autosize if (fontSize == 0) { - for (fontSize = 20; fontSize > 1; --fontSize) { - const double availableWidthInFontSize = wMax / fontSize; - double y = dy - 3; - int i = 0; - while (i < text->getLength()) { - GooString lineText(text->toStr().substr(i)); - if (!lineText.hasUnicodeMarker() && isUnicode) { - lineText.prependUnicodeMarker(); - } - const HorizontalTextLayouter textLayouter(&lineText, form, font, availableWidthInFontSize, forceZapfDingbats); - y -= fontSize; - if (i == 0) { - i += textLayouter.consumedText; - } else { - i += textLayouter.consumedText - (isUnicode ? 2 : 0); - } - } - // approximate the descender for the last line - if (y >= 0.33 * fontSize) { - break; - } - } + fontSize = Annot::calculateFontSize(form, font, text, wMax, dy, forceZapfDingbats); daToks[tfPos + 1] = GooString().appendf("{0:.2f}", fontSize)->toStr(); } @@ -4804,9 +4826,6 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c if (flags & EmitMarkedContentDrawTextFlag) { appearBuf->append("EMC\n"); } - if (freeText) { - delete text; - } return true; } @@ -4815,7 +4834,7 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const Form *form, c bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, VariableTextQuadding quadding, XRef *xref, Dict *resourcesDict) { - std::vector daToks; + std::vector> daToks; GooString *tok; GooString convertedText; const GfxFont *font; @@ -4839,7 +4858,7 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con for (j = i + 1; j < da->getLength() && !Lexer::isSpace(da->getChar(j)); ++j) { ; } - daToks.push_back(new GooString(da, i, j - i)); + daToks.push_back(std::make_unique(da, i, j - i)); i = j; } } @@ -4856,7 +4875,7 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con font = nullptr; fontSize = 0; if (tfPos >= 0) { - tok = daToks[tfPos]; + tok = daToks[tfPos].get(); if (tok->getLength() >= 1 && tok->getChar(0) == '/') { if (!resources || !(font = resources->lookupFont(tok->c_str() + 1).get())) { if (xref != nullptr && resourcesDict != nullptr) { @@ -4874,15 +4893,12 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con } else { error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string"); } - tok = daToks[tfPos + 1]; + tok = daToks[tfPos + 1].get(); fontSize = gatof(tok->c_str()); } else { error(errSyntaxError, -1, "Missing 'Tf' operator in field's DA string"); } if (!font) { - for (auto entry : daToks) { - delete entry; - } return false; } @@ -4896,9 +4912,6 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con j = 0; if (fieldChoice->getChoice(i) == nullptr) { error(errSyntaxError, -1, "Invalid annotation listbox"); - for (auto entry : daToks) { - delete entry; - } return false; } double w; @@ -4914,7 +4927,7 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con } fontSize = floor(fontSize); if (tfPos >= 0) { - tok = daToks[tfPos + 1]; + tok = daToks[tfPos + 1].get(); tok->clear(); tok->appendf("{0:.2f}", fontSize); } @@ -4954,17 +4967,17 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con // set the font matrix if (tmPos >= 0) { - tok = daToks[tmPos + 4]; + tok = daToks[tmPos + 4].get(); tok->clear(); tok->appendf("{0:.2f}", x); - tok = daToks[tmPos + 5]; + tok = daToks[tmPos + 5].get(); tok->clear(); tok->appendf("{0:.2f}", y); } // write the DA string - for (const GooString *daTok : daToks) { - appearBuf->append(daTok)->append(' '); + for (const std::unique_ptr &daTok : daToks) { + appearBuf->append(daTok.get())->append(' '); } // write the font matrix (if not part of the DA string) @@ -4989,10 +5002,6 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con y -= 1.1 * fontSize; } - for (auto entry : daToks) { - delete entry; - } - return true; } @@ -5027,21 +5036,21 @@ void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const Annot case AnnotBorder::borderSolid: case AnnotBorder::borderUnderlined: appearBuf->appendf("{0:.2f} w\n", w); - setDrawColor(aColor, false); + setDrawColor(*aColor, false); drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, false); break; case AnnotBorder::borderBeveled: case AnnotBorder::borderInset: appearBuf->appendf("{0:.2f} w\n", 0.5 * w); - setDrawColor(aColor, false); + setDrawColor(*aColor, false); drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, false); adjustedColor = AnnotColor(*aColor); adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1); - setDrawColor(&adjustedColor, false); + setDrawColor(adjustedColor, false); drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w); adjustedColor = AnnotColor(*aColor); adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1); - setDrawColor(&adjustedColor, false); + setDrawColor(adjustedColor, false); drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w); break; } @@ -5056,14 +5065,14 @@ void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const Annot // fallthrough case AnnotBorder::borderSolid: appearBuf->appendf("{0:.2f} w\n", w); - setDrawColor(aColor, false); + setDrawColor(*aColor, false); appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n", 0.5 * w, dx - w, dy - w); break; case AnnotBorder::borderBeveled: case AnnotBorder::borderInset: adjustedColor = AnnotColor(*aColor); adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? 1 : -1); - setDrawColor(&adjustedColor, true); + setDrawColor(adjustedColor, true); appearBuf->append("0 0 m\n"); appearBuf->appendf("0 {0:.2f} l\n", dy); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy); @@ -5073,7 +5082,7 @@ void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const Annot appearBuf->append("f\n"); adjustedColor = AnnotColor(*aColor); adjustedColor.adjustColor(border->getStyle() == AnnotBorder::borderBeveled ? -1 : 1); - setDrawColor(&adjustedColor, true); + setDrawColor(adjustedColor, true); appearBuf->append("0 0 m\n"); appearBuf->appendf("{0:.2f} 0 l\n", dx); appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy); @@ -5084,7 +5093,7 @@ void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const Annot break; case AnnotBorder::borderUnderlined: appearBuf->appendf("{0:.2f} w\n", w); - setDrawColor(aColor, false); + setDrawColor(*aColor, false); appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx); break; } @@ -5137,7 +5146,7 @@ bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, c if (aColor) { const double dx = rect->x2 - rect->x1; const double dy = rect->y2 - rect->y1; - setDrawColor(aColor, true); + setDrawColor(*aColor, true); drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy), true); } return true; @@ -5221,22 +5230,48 @@ bool AnnotAppearanceBuilder::drawSignatureFieldText(const FormFieldSignature *fi Matrix matrix = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }; matrix.scale(width, height); static const char *IMG_TMPL = "\nq {0:.1g} {1:.1g} {2:.1g} {3:.1g} {4:.1g} {5:.1g} cm /{6:s} Do Q\n"; - const std::unique_ptr imgBuffer = GooString::format(IMG_TMPL, matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[5], imageResourceId); - append(imgBuffer->c_str()); + const std::string imgBuffer = GooString::format(IMG_TMPL, matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[5], imageResourceId); + append(imgBuffer.c_str()); } const GooString &leftText = field->getCustomAppearanceLeftContent(); if (leftText.toStr().empty()) { drawSignatureFieldText(contents, form, DefaultAppearance(_da), border, rect, xref, resourcesDict, 0, false /* don't center vertically */, false /* don't center horizontally */); } else { - DefaultAppearance daLeft(_da); - daLeft.setFontPtSize(field->getCustomAppearanceLeftFontSize()); const double halfWidth = (rect->x2 - rect->x1) / 2; + + double borderWidth = 0; + + if (border) { + borderWidth = border->getWidth(); + } + + const double wMax = (rect->x2 - rect->x1) - 2 * borderWidth - 4; + const double hMax = (rect->y2 - rect->y1) - 2 * borderWidth; + + DefaultAppearance daLeft(_da); + + double leftFontSize = field->getCustomAppearanceLeftFontSize(); + if (leftFontSize == 0) { + std::shared_ptr font = form->getDefaultResources()->lookupFont(daLeft.getFontName().getName()); + leftFontSize = Annot::calculateFontSize(form, font.get(), &leftText, wMax / 2.0, hMax); + } + daLeft.setFontPtSize(leftFontSize); + PDFRectangle rectLeft(rect->x1, rect->y1, rect->x1 + halfWidth, rect->y2); drawSignatureFieldText(leftText, form, daLeft, border, &rectLeft, xref, resourcesDict, 0, true /* center vertically */, true /* center horizontally */); + DefaultAppearance daRight(_da); + + double fontSize = daRight.getFontPtSize(); + if (fontSize == 0) { + std::shared_ptr font = form->getDefaultResources()->lookupFont(daLeft.getFontName().getName()); + fontSize = Annot::calculateFontSize(form, font.get(), &contents, wMax / 2.0, hMax); + } + daRight.setFontPtSize(fontSize); + PDFRectangle rectRight(rectLeft.x2, rect->y1, rect->x2, rect->y2); - drawSignatureFieldText(contents, form, DefaultAppearance(_da), border, &rectRight, xref, resourcesDict, halfWidth, true /* center vertically */, false /* don't center horizontally */); + drawSignatureFieldText(contents, form, daRight, border, &rectRight, xref, resourcesDict, halfWidth, true /* center vertically */, false /* don't center horizontally */); } return true; @@ -5251,7 +5286,7 @@ void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const if (border) { borderWidth = border->getWidth(); if (borderWidth > 0) { - setLineStyleForBorder(border); + setLineStyleForBorder(*border); } } @@ -5269,7 +5304,7 @@ void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const // Setup text clipping appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re W n\n", leftMargin + textmargin, textmargin, textwidth, height - 2 * textmargin); - setDrawColor(da.getFontColor(), true); + setDrawColor(*da.getFontColor(), true); const DrawMultiLineTextResult textCommands = drawMultiLineText(text, textwidth, form, *font, da.getFontName().getName(), da.getFontPtSize(), centerHorizontally ? VariableTextQuadding::centered : VariableTextQuadding::leftJustified, 0 /*borderWidth*/); @@ -5277,7 +5312,7 @@ void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const if (centerVertically) { const double outTextHeight = textCommands.nLines * da.getFontPtSize(); if (outTextHeight < height) { - yDelta -= (height - outTextHeight) / 2; + yDelta = height - (height - outTextHeight) / 2; } } appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", leftMargin + textmargin, yDelta); @@ -5300,7 +5335,7 @@ bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice *fieldCho } if (fieldChoice->isCombo()) { - selected = fieldChoice->getSelectedChoice(); + selected = fieldChoice->getAppearanceSelectedChoice(); if (selected) { return drawText(selected, form, da, resources, border, appearCharacs, rect, quadding, xref, resourcesDict, EmitMarkedContentDrawTextFlag); //~ Acrobat draws a popup icon on the right side @@ -5313,19 +5348,8 @@ bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice *fieldCho return true; } -static bool insertIfNotAlreadyPresent(Ref r, std::set *alreadySeenDicts) -{ - if (r == Ref::INVALID()) { - return true; - } - - // std::pair - const auto insertResult = alreadySeenDicts->insert(r.num); - return insertResult.second; -} - // Should we also merge Arrays? -static void recursiveMergeDicts(Dict *primary, const Dict *secondary, std::set *alreadySeenDicts) +static void recursiveMergeDicts(Dict *primary, const Dict *secondary, RefRecursionChecker *alreadySeenDicts) { for (int i = 0; i < secondary->getLength(); ++i) { const char *key = secondary->getKey(i); @@ -5338,7 +5362,7 @@ static void recursiveMergeDicts(Dict *primary, const Dict *secondary, std::setlookup(key, &secondaryRef); if (secondaryObj.isDict()) { - if (!insertIfNotAlreadyPresent(primaryRef, alreadySeenDicts) || !insertIfNotAlreadyPresent(secondaryRef, alreadySeenDicts)) { + if (!alreadySeenDicts->insert(primaryRef) || !alreadySeenDicts->insert(secondaryRef)) { // bad PDF return; } @@ -5351,7 +5375,7 @@ static void recursiveMergeDicts(Dict *primary, const Dict *secondary, std::set alreadySeenDicts; + RefRecursionChecker alreadySeenDicts; recursiveMergeDicts(primary, secondary, &alreadySeenDicts); } @@ -5365,7 +5389,7 @@ void AnnotWidget::generateFieldAppearance() if (appearCharacs) { const AnnotColor *aColor = appearCharacs->getBackColor(); if (aColor) { - appearBuilder.setDrawColor(aColor, true); + appearBuilder.setDrawColor(*aColor, true); appearBuilder.appendf("0 0 {0:.2f} {1:.2f} re f\n", rect->x2 - rect->x1, rect->y2 - rect->y1); } } @@ -5388,7 +5412,7 @@ void AnnotWidget::generateFieldAppearance() // And yet Acrobat Reader seems to be taking a field's DR into account. Object resourcesDictObj; const GfxResources *resources = nullptr; - GfxResources *resourcesToFree = nullptr; + std::unique_ptr resourcesToFree = nullptr; if (field->getObj() && field->getObj()->isDict()) { // Let's use a field's resource dictionary. resourcesDictObj = field->getObj()->dictLookup("DR"); @@ -5397,8 +5421,8 @@ void AnnotWidget::generateFieldAppearance() resourcesDictObj = resourcesDictObj.deepCopy(); recursiveMergeDicts(resourcesDictObj.getDict(), form->getDefaultResourcesObj()->getDict()); } - resourcesToFree = new GfxResources(doc->getXRef(), resourcesDictObj.getDict(), nullptr); - resources = resourcesToFree; + resourcesToFree = std::make_unique(doc->getXRef(), resourcesDictObj.getDict(), nullptr); + resources = resourcesToFree.get(); } } if (!resourcesDictObj.isDict()) { @@ -5440,14 +5464,12 @@ void AnnotWidget::generateFieldAppearance() // We should technically do this for all annots but AnnotFreeText // forms are particularly special since we're potentially embeddeing a font so we really need // to set the AP and not let other renderers guess it from the contents - setNewAppearance(Object(appearStream)); + // In addition, we keep the appearState of checkboxes to prevent them from being deselected + bool keepAppearState = field->getType() == formButton && static_cast(field)->getButtonType() == formButtonCheck; + setNewAppearance(Object(appearStream), keepAppearState); } else { appearance = Object(appearStream); } - - if (resourcesToFree) { - delete resourcesToFree; - } } void AnnotWidget::updateAppearanceStream() @@ -5460,8 +5482,12 @@ void AnnotWidget::updateAppearanceStream() // There's no need to create a new appearance stream if NeedAppearances is // set, because it will be ignored next time anyway. + // except if signature type; most readers can't figure out how to create an + // appearance for those and thus renders nothing. if (form && form->getNeedAppearances()) { - return; + if (field->getType() != FormFieldType::formSignature) { + return; + } } // Create the new appearance @@ -5501,9 +5527,9 @@ void AnnotWidget::draw(Gfx *gfx, bool printing) // Only construct the appearance stream when // - annot doesn't have an AP or - // - NeedAppearances is true + // - NeedAppearances is true and it isn't a Signature. There isn't enough data in our objects to generate it for signatures if (field) { - if (appearance.isNull() || (form && form->getNeedAppearances())) { + if (appearance.isNull() || (field->getType() != FormFieldType::formSignature && form && form->getNeedAppearances())) { generateFieldAppearance(); } } @@ -5547,7 +5573,7 @@ void AnnotMovie::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("T"); if (obj1.isString()) { - title.reset(obj1.getString()->copy()); + title = obj1.getString()->copy(); } Object movieDict = dict->lookup("Movie"); @@ -5669,7 +5695,7 @@ void AnnotScreen::initialize(PDFDoc *docA, Dict *dict) obj1 = dict->lookup("T"); if (obj1.isString()) { - title.reset(obj1.getString()->copy()); + title = obj1.getString()->copy(); } obj1 = dict->lookup("A"); @@ -5752,6 +5778,35 @@ void AnnotStamp::generateStampCustomAppearance() const double bboxArray[4] = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 }; const GooString *appearBuf = appearBuilder.buffer(); appearance = createForm(appearBuf, bboxArray, false, resDict); + + if (updatedAppearanceStream == Ref::INVALID()) { + updatedAppearanceStream = doc->getXRef()->addIndirectObject(appearance); + } else { + Object obj1 = appearance.fetch(doc->getXRef()); + doc->getXRef()->setModifiedObject(&obj1, updatedAppearanceStream); + } + + Object obj1 = Object(new Dict(doc->getXRef())); + obj1.dictAdd("N", Object(updatedAppearanceStream)); + update("AP", std::move(obj1)); +} + +void AnnotStamp::updateAppearanceResDict() +{ + if (appearance.isNull()) { + if (stampImageHelper != nullptr) { + generateStampCustomAppearance(); + } else { + generateStampDefaultAppearance(); + } + } +} + +Object AnnotStamp::getAppearanceResDict() +{ + updateAppearanceResDict(); + + return Annot::getAppearanceResDict(); } void AnnotStamp::generateStampDefaultAppearance() @@ -5835,8 +5890,8 @@ void AnnotStamp::generateStampDefaultAppearance() } const double bboxArray[4] = { 0, 0, rect->x2 - rect->x1, rect->y2 - rect->y1 }; - const std::unique_ptr scale = GooString::format("{0:.6g} 0 0 {1:.6g} 0 0 cm\nq\n", bboxArray[2] / stampUnscaledWidth, bboxArray[3] / stampUnscaledHeight); - defaultAppearanceBuilder.append(scale->c_str()); + const std::string scale = GooString::format("{0:.6g} 0 0 {1:.6g} 0 0 cm\nq\n", bboxArray[2] / stampUnscaledWidth, bboxArray[3] / stampUnscaledHeight); + defaultAppearanceBuilder.append(scale.c_str()); defaultAppearanceBuilder.append(stampCode); defaultAppearanceBuilder.append("Q\n"); @@ -5858,13 +5913,9 @@ void AnnotStamp::draw(Gfx *gfx, bool printing) } annotLocker(); - if (appearance.isNull()) { - if (stampImageHelper != nullptr) { - generateStampCustomAppearance(); - } else { - generateStampDefaultAppearance(); - } - } + + // generate the appearance stream + updateAppearanceResDict(); // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); @@ -5897,18 +5948,10 @@ void AnnotStamp::setCustomImage(AnnotStampImageHelper *stampImageHelperA) clearCustomImage(); stampImageHelper = stampImageHelperA; - generateStampCustomAppearance(); - if (updatedAppearanceStream == Ref::INVALID()) { - updatedAppearanceStream = doc->getXRef()->addIndirectObject(appearance); - } else { - Object obj1 = appearance.fetch(doc->getXRef()); - doc->getXRef()->setModifiedObject(&obj1, updatedAppearanceStream); - } - - Object obj1 = Object(new Dict(doc->getXRef())); - obj1.dictAdd("N", Object(updatedAppearanceStream)); - update("AP", std::move(obj1)); + // Regenerate appearance stream + invalidateAppearance(); + updateAppearanceResDict(); } void AnnotStamp::clearCustomImage() @@ -6014,6 +6057,7 @@ void AnnotGeometry::setInteriorColor(std::unique_ptr &&new_color) interiorColor = std::move(new_color); } else { interiorColor = nullptr; + update("IC", Object(objNull)); } invalidateAppearance(); } @@ -6034,14 +6078,14 @@ void AnnotGeometry::draw(Gfx *gfx, bool printing) AnnotAppearanceBuilder appearBuilder; appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } double borderWidth = border->getWidth(); - appearBuilder.setLineStyleForBorder(border.get()); + appearBuilder.setLineStyleForBorder(*border); if (interiorColor) { - appearBuilder.setDrawColor(interiorColor.get(), true); + appearBuilder.setDrawColor(*interiorColor, true); } if (type == typeSquare) { @@ -6213,12 +6257,12 @@ void AnnotPolygon::setType(AnnotSubtype new_type) invalidateAppearance(); } -void AnnotPolygon::setVertices(AnnotPath *path) +void AnnotPolygon::setVertices(const AnnotPath &path) { Array *a = new Array(doc->getXRef()); - for (int i = 0; i < path->getCoordsLength(); i++) { - a->add(Object(path->getX(i))); - a->add(Object(path->getY(i))); + for (int i = 0; i < path.getCoordsLength(); i++) { + a->add(Object(path.getX(i))); + a->add(Object(path.getY(i))); } vertices = std::make_unique(a); @@ -6246,6 +6290,9 @@ void AnnotPolygon::setInteriorColor(std::unique_ptr &&new_color) Object obj1 = new_color->writeToObject(doc->getXRef()); update("IC", std::move(obj1)); interiorColor = std::move(new_color); + } else { + interiorColor = nullptr; + update("IC", Object(objNull)); } invalidateAppearance(); } @@ -6357,14 +6404,14 @@ void AnnotPolygon::draw(Gfx *gfx, bool printing) appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } - appearBuilder.setLineStyleForBorder(border.get()); + appearBuilder.setLineStyleForBorder(*border); appearBBox->setBorderWidth(std::max(1., border->getWidth())); if (interiorColor) { - appearBuilder.setDrawColor(interiorColor.get(), true); + appearBuilder.setDrawColor(*interiorColor, true); } if (type == typePolyLine) { @@ -6488,10 +6535,7 @@ AnnotInk::AnnotInk(PDFDoc *docA, Object &&dictObject, const Object *obj) : Annot initialize(docA, annotObj.getDict()); } -AnnotInk::~AnnotInk() -{ - freeInkList(); -} +AnnotInk::~AnnotInk() = default; void AnnotInk::initialize(PDFDoc *docA, Dict *dict) { @@ -6501,8 +6545,6 @@ void AnnotInk::initialize(PDFDoc *docA, Dict *dict) if (obj1.isArray()) { parseInkList(obj1.getArray()); } else { - inkListLength = 0; - inkList = nullptr; error(errSyntaxError, -1, "Bad Annot Ink List"); obj1 = dict->lookup("AP"); @@ -6522,10 +6564,9 @@ void AnnotInk::initialize(PDFDoc *docA, Dict *dict) } } -void AnnotInk::writeInkList(AnnotPath **paths, int n_paths, Array *dest_array) +void AnnotInk::writeInkList(const std::vector> &paths, Array *dest_array) { - for (int i = 0; i < n_paths; ++i) { - AnnotPath *path = paths[i]; + for (const auto &path : paths) { Array *a = new Array(doc->getXRef()); for (int j = 0; j < path->getCoordsLength(); ++j) { a->add(Object(path->getX(j))); @@ -6537,33 +6578,23 @@ void AnnotInk::writeInkList(AnnotPath **paths, int n_paths, Array *dest_array) void AnnotInk::parseInkList(Array *array) { - inkListLength = array->getLength(); - inkList = (AnnotPath **)gmallocn((inkListLength), sizeof(AnnotPath *)); - memset(inkList, 0, inkListLength * sizeof(AnnotPath *)); + int inkListLength = array->getLength(); + inkList.clear(); + inkList.reserve(inkListLength); for (int i = 0; i < inkListLength; i++) { Object obj2 = array->get(i); if (obj2.isArray()) { - inkList[i] = new AnnotPath(obj2.getArray()); - } - } -} - -void AnnotInk::freeInkList() -{ - if (inkList) { - for (int i = 0; i < inkListLength; ++i) { - delete inkList[i]; + inkList.push_back(std::make_unique(obj2.getArray())); + } else { + inkList.emplace_back(); } - gfree(inkList); } } -void AnnotInk::setInkList(AnnotPath **paths, int n_paths) +void AnnotInk::setInkList(const std::vector> &paths) { - freeInkList(); - Array *a = new Array(doc->getXRef()); - writeInkList(paths, n_paths, a); + writeInkList(paths, a); parseInkList(a); annotObj.dictSet("InkList", Object(a)); @@ -6587,14 +6618,13 @@ void AnnotInk::draw(Gfx *gfx, bool printing) appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), false); + appearBuilder.setDrawColor(*color, false); } - appearBuilder.setLineStyleForBorder(border.get()); + appearBuilder.setLineStyleForBorder(*border); appearBBox->setBorderWidth(std::max(1., border->getWidth())); - for (int i = 0; i < inkListLength; ++i) { - const AnnotPath *path = inkList[i]; + for (const auto &path : inkList) { if (path && path->getCoordsLength() != 0) { appearBuilder.appendf("{0:.2f} {1:.2f} m\n", path->getX(0) - rect->x1, path->getY(0) - rect->y1); appearBBox->extendTo(path->getX(0) - rect->x1, path->getY(0) - rect->y1); @@ -6799,7 +6829,7 @@ void AnnotFileAttachment::draw(Gfx *gfx, bool printing) appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), true); + appearBuilder.setDrawColor(*color, true); } else { appearBuilder.append("1 1 1 rg\n"); } @@ -6952,7 +6982,7 @@ void AnnotSound::draw(Gfx *gfx, bool printing) appearBuilder.append("q\n"); if (color) { - appearBuilder.setDrawColor(color.get(), true); + appearBuilder.setDrawColor(*color, true); } else { appearBuilder.append("1 1 1 rg\n"); } @@ -7207,32 +7237,26 @@ AnnotRichMedia::Content::Content(Dict *dict) { Object obj1 = dict->lookup("Configurations"); if (obj1.isArray()) { - nConfigurations = obj1.arrayGetLength(); - - configurations = (Configuration **)gmallocn(nConfigurations, sizeof(Configuration *)); + int numberOfConfigurations = obj1.arrayGetLength(); + configurations.reserve(numberOfConfigurations); - for (int i = 0; i < nConfigurations; ++i) { + for (int i = 0; i < numberOfConfigurations; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isDict()) { - configurations[i] = new AnnotRichMedia::Configuration(obj2.getDict()); + configurations.push_back(std::make_unique(obj2.getDict())); } else { - configurations[i] = nullptr; + configurations.emplace_back(); } } - } else { - nConfigurations = 0; - configurations = nullptr; } - nAssets = 0; - assets = nullptr; obj1 = dict->lookup("Assets"); if (obj1.isDict()) { Object obj2 = obj1.getDict()->lookup("Names"); if (obj2.isArray()) { const int length = obj2.arrayGetLength() / 2; + assets.reserve(length); - assets = (Asset **)gmallocn(length, sizeof(Asset *)); for (int i = 0; i < length; ++i) { Object objKey = obj2.arrayGet(2 * i); Object objVal = obj2.arrayGet(2 * i + 1); @@ -7242,58 +7266,45 @@ AnnotRichMedia::Content::Content(Dict *dict) continue; } - assets[nAssets] = new AnnotRichMedia::Asset; - assets[nAssets]->name = std::make_unique(objKey.getString()); - assets[nAssets]->fileSpec = std::move(objVal); - ++nAssets; + auto asset = std::make_unique(); + + asset->name = std::make_unique(objKey.getString()); + asset->fileSpec = std::move(objVal); + assets.push_back(std::move(asset)); } } } + assets.shrink_to_fit(); } -AnnotRichMedia::Content::~Content() -{ - if (configurations) { - for (int i = 0; i < nConfigurations; ++i) { - delete configurations[i]; - } - gfree(configurations); - } - - if (assets) { - for (int i = 0; i < nAssets; ++i) { - delete assets[i]; - } - gfree(assets); - } -} +AnnotRichMedia::Content::~Content() = default; int AnnotRichMedia::Content::getConfigurationsCount() const { - return nConfigurations; + return configurations.size(); } AnnotRichMedia::Configuration *AnnotRichMedia::Content::getConfiguration(int index) const { - if (index < 0 || index >= nConfigurations) { + if (index < 0 || index >= (int)configurations.size()) { return nullptr; } - return configurations[index]; + return configurations[index].get(); } int AnnotRichMedia::Content::getAssetsCount() const { - return nAssets; + return assets.size(); } AnnotRichMedia::Asset *AnnotRichMedia::Content::getAsset(int index) const { - if (index < 0 || index >= nAssets) { + if (index < 0 || index >= (int)assets.size()) { return nullptr; } - return assets[index]; + return assets[index].get(); } AnnotRichMedia::Asset::Asset() = default; @@ -7314,20 +7325,17 @@ AnnotRichMedia::Configuration::Configuration(Dict *dict) { Object obj1 = dict->lookup("Instances"); if (obj1.isArray()) { - nInstances = obj1.arrayGetLength(); + int numberOfInstances = obj1.arrayGetLength(); + instances.reserve(numberOfInstances); - instances = (Instance **)gmallocn(nInstances, sizeof(Instance *)); - - for (int i = 0; i < nInstances; ++i) { + for (int i = 0; i < numberOfInstances; ++i) { Object obj2 = obj1.arrayGet(i); if (obj2.isDict()) { - instances[i] = new AnnotRichMedia::Instance(obj2.getDict()); + instances.push_back(std::make_unique(obj2.getDict())); } else { - instances[i] = nullptr; + instances.emplace_back(); } } - } else { - instances = nullptr; } obj1 = dict->lookup("Name"); @@ -7350,55 +7358,44 @@ AnnotRichMedia::Configuration::Configuration(Dict *dict) } else { // determine from first non null instance type = typeFlash; // default in case all instances are null - if (instances && nInstances > 0) { - for (int i = 0; i < nInstances; ++i) { - AnnotRichMedia::Instance *instance = instances[i]; - if (instance) { - switch (instance->getType()) { - case AnnotRichMedia::Instance::type3D: - type = type3D; - break; - case AnnotRichMedia::Instance::typeFlash: - type = typeFlash; - break; - case AnnotRichMedia::Instance::typeSound: - type = typeSound; - break; - case AnnotRichMedia::Instance::typeVideo: - type = typeVideo; - break; - } - // break the loop since we found the first non null instance + for (auto &instance : instances) { + if (instance) { + switch (instance->getType()) { + case AnnotRichMedia::Instance::type3D: + type = type3D; + break; + case AnnotRichMedia::Instance::typeFlash: + type = typeFlash; + break; + case AnnotRichMedia::Instance::typeSound: + type = typeSound; + break; + case AnnotRichMedia::Instance::typeVideo: + type = typeVideo; break; } + // break the loop since we found the first non null instance + break; } } } } } -AnnotRichMedia::Configuration::~Configuration() -{ - if (instances) { - for (int i = 0; i < nInstances; ++i) { - delete instances[i]; - } - gfree(instances); - } -} +AnnotRichMedia::Configuration::~Configuration() = default; int AnnotRichMedia::Configuration::getInstancesCount() const { - return nInstances; + return instances.size(); } AnnotRichMedia::Instance *AnnotRichMedia::Configuration::getInstance(int index) const { - if (index < 0 || index >= nInstances) { + if (index < 0 || index >= (int)instances.size()) { return nullptr; } - return instances[index]; + return instances[index].get(); } const GooString *AnnotRichMedia::Configuration::getName() const @@ -7503,7 +7500,7 @@ void Annots::appendAnnot(Annot *annot) bool Annots::removeAnnot(Annot *annot) { - auto idx = std::find(annots.begin(), annots.end(), annot); + auto idx = std::ranges::find(annots, annot); if (idx == annots.end()) { return false; @@ -7624,12 +7621,9 @@ Annots::~Annots() // AnnotAppearanceBuilder //------------------------------------------------------------------------ -AnnotAppearanceBuilder::AnnotAppearanceBuilder() : appearBuf(new GooString()) { } +AnnotAppearanceBuilder::AnnotAppearanceBuilder() : appearBuf { std::make_unique() } { } -AnnotAppearanceBuilder::~AnnotAppearanceBuilder() -{ - delete appearBuf; -} +AnnotAppearanceBuilder::~AnnotAppearanceBuilder() = default; void AnnotAppearanceBuilder::append(const char *text) { @@ -7647,5 +7641,5 @@ void AnnotAppearanceBuilder::appendf(const char *fmt, ...) GOOSTRING_FORMAT const GooString *AnnotAppearanceBuilder::buffer() const { - return appearBuf; + return appearBuf.get(); } diff --git a/poppler/Annot.h b/poppler/Annot.h index 819877d..66385b6 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -21,11 +21,11 @@ // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008 Tomas Are Haavet -// Copyright (C) 2009-2011, 2013, 2016-2022 Albert Astals Cid +// Copyright (C) 2009-2011, 2013, 2016-2024 Albert Astals Cid // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2012, 2015 Tobias Koenig // Copyright (C) 2013 Thomas Freitag -// Copyright (C) 2013, 2017 Adrian Johnson +// Copyright (C) 2013, 2017, 2023 Adrian Johnson // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Dileep Sankhla // Copyright (C) 2018-2020 Tobias Deiminger @@ -42,6 +42,10 @@ // Copyright (C) 2021 Mahmoud Ahmed Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2022 Martin +// Copyright (C) 2024 Erich E. Hoover +// Copyright (C) 2024 Carsten Emde +// Copyright (C) 2024 Lucas Baudin +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -141,7 +145,6 @@ class POPPLER_PRIVATE_EXPORT AnnotPath double getX(int coord) const; double getY(int coord) const; - AnnotCoord *getCoord(int coord); int getCoordsLength() const { return coords.size(); } protected: @@ -235,14 +238,14 @@ class POPPLER_PRIVATE_EXPORT AnnotQuadrilaterals AnnotQuadrilaterals(const AnnotQuadrilaterals &) = delete; AnnotQuadrilaterals &operator=(const AnnotQuadrilaterals &other) = delete; - double getX1(int quadrilateral); - double getY1(int quadrilateral); - double getX2(int quadrilateral); - double getY2(int quadrilateral); - double getX3(int quadrilateral); - double getY3(int quadrilateral); - double getX4(int quadrilateral); - double getY4(int quadrilateral); + double getX1(int quadrilateral) const; + double getY1(int quadrilateral) const; + double getX2(int quadrilateral) const; + double getY2(int quadrilateral) const; + double getX3(int quadrilateral) const; + double getY3(int quadrilateral) const; + double getX4(int quadrilateral) const; + double getY4(int quadrilateral) const; int getQuadrilateralsLength() const { return quadrilateralsLength; } protected: @@ -577,8 +580,8 @@ class AnnotAppearanceBuilder AnnotAppearanceBuilder(const AnnotAppearanceBuilder &) = delete; AnnotAppearanceBuilder &operator=(const AnnotAppearanceBuilder &) = delete; - void setDrawColor(const AnnotColor *color, bool fill); - void setLineStyleForBorder(const AnnotBorder *border); + void setDrawColor(const AnnotColor &color, bool fill); + void setLineStyleForBorder(const AnnotBorder &border); void setTextFont(const Object &fontName, double fontSize); void drawCircle(double cx, double cy, double r, bool fill); void drawEllipse(double cx, double cy, double rx, double ry, bool fill, bool stroke); @@ -627,7 +630,7 @@ class AnnotAppearanceBuilder const VariableTextQuadding quadding, XRef *xref, Dict *resourcesDict, const int flags = NoDrawTextFlags, const int nCombs = 0); void drawArrowPath(double x, double y, const Matrix &m, int orientation = 1); - GooString *appearBuf; + std::unique_ptr appearBuf; }; //------------------------------------------------------------------------ @@ -716,6 +719,8 @@ class POPPLER_PRIVATE_EXPORT Annot Annot(PDFDoc *docA, Object &&dictObject, const Object *obj); bool isOk() { return ok; } + static double calculateFontSize(const Form *form, const GfxFont *font, const GooString *text, const double wMax, const double hMax, const bool forceZapfDingbats = {}); + void incRefCnt(); void decRefCnt(); @@ -730,14 +735,14 @@ class POPPLER_PRIVATE_EXPORT Annot double getXMax(); double getYMax(); - void setRect(const PDFRectangle *rect); + void setRect(const PDFRectangle &rect); void setRect(double x1, double y1, double x2, double y2); // Sets the annot contents to new_content // new_content should never be NULL virtual void setContents(std::unique_ptr &&new_content); void setName(GooString *new_name); - void setModified(GooString *new_modified); + void setModified(std::unique_ptr new_modified); void setFlags(unsigned int new_flags); void setBorder(std::unique_ptr &&new_border); @@ -760,6 +765,7 @@ class POPPLER_PRIVATE_EXPORT Annot unsigned int getFlags() const { return flags; } Object getAppearance() const; void setNewAppearance(Object &&newAppearance); + void setNewAppearance(Object &&newAppearance, bool keepAppearState); AnnotAppearance *getAppearStreams() const { return appearStreams.get(); } const GooString *getAppearState() const { return appearState.get(); } AnnotBorder *getBorder() const { return border.get(); } @@ -887,7 +893,7 @@ class POPPLER_PRIVATE_EXPORT AnnotMarkup : public Annot void setPopup(std::unique_ptr &&new_popup); void setLabel(std::unique_ptr &&new_label); void setOpacity(double opacityA); - void setDate(GooString *new_date); + void setDate(std::unique_ptr new_date); protected: void removeReferencedObjects() override; @@ -1196,7 +1202,7 @@ class POPPLER_PRIVATE_EXPORT AnnotTextMarkup : public AnnotMarkup // typeHighlight, typeUnderline, typeSquiggly or typeStrikeOut void setType(AnnotSubtype new_type); - void setQuadrilaterals(AnnotQuadrilaterals *quadPoints); + void setQuadrilaterals(const AnnotQuadrilaterals &quadPoints); AnnotQuadrilaterals *getQuadrilaterals() const { return quadrilaterals.get(); } @@ -1231,10 +1237,13 @@ class POPPLER_PRIVATE_EXPORT AnnotStamp : public AnnotMarkup // getters const GooString *getIcon() const { return icon.get(); } + Object getAppearanceResDict() override; + private: void initialize(PDFDoc *docA, Dict *dict); void generateStampDefaultAppearance(); void generateStampCustomAppearance(); + void updateAppearanceResDict(); std::unique_ptr icon; // Name (Default Draft) AnnotStampImageHelper *stampImageHelper; @@ -1291,7 +1300,7 @@ class POPPLER_PRIVATE_EXPORT AnnotPolygon : public AnnotMarkup void draw(Gfx *gfx, bool printing) override; void generatePolyLineAppearance(AnnotAppearanceBuilder *appearBuilder); void setType(AnnotSubtype new_type); // typePolygon or typePolyLine - void setVertices(AnnotPath *path); + void setVertices(const AnnotPath &path); void setStartEndStyle(AnnotLineEndingStyle start, AnnotLineEndingStyle end); void setInteriorColor(std::unique_ptr &&new_color); void setIntent(AnnotPolygonIntent new_intent); @@ -1364,21 +1373,18 @@ class POPPLER_PRIVATE_EXPORT AnnotInk : public AnnotMarkup void draw(Gfx *gfx, bool printing) override; - void setInkList(AnnotPath **paths, int n_paths); + void setInkList(const std::vector> &paths); // getters - AnnotPath **getInkList() const { return inkList; } - int getInkListLength() const { return inkListLength; } + const std::vector> &getInkList() const { return inkList; } private: void initialize(PDFDoc *docA, Dict *dict); - void writeInkList(AnnotPath **paths, int n_paths, Array *dest_array); + void writeInkList(const std::vector> &paths, Array *dest_array); void parseInkList(Array *src_array); - void freeInkList(); // required - AnnotPath **inkList; // InkList - int inkListLength; + std::vector> inkList; // InkList // optional // inherited from Annot @@ -1472,7 +1478,7 @@ class POPPLER_PRIVATE_EXPORT AnnotWidget : public Annot std::unique_ptr getFormAdditionalAction(FormAdditionalActionsType type); Dict *getParent() { return parent; } - bool setFormAdditionalAction(FormAdditionalActionsType type, const GooString &js); + bool setFormAdditionalAction(FormAdditionalActionsType type, const std::string &js); void setField(FormField *f) { field = f; }; @@ -1630,8 +1636,7 @@ class POPPLER_PRIVATE_EXPORT AnnotRichMedia : public Annot // optional Type type; // Subtype std::unique_ptr name; // Name - Instance **instances; // Instances - int nInstances; + std::vector> instances; // Instances }; class Content; @@ -1672,11 +1677,9 @@ class POPPLER_PRIVATE_EXPORT AnnotRichMedia : public Annot private: // optional - Configuration **configurations; // Configurations - int nConfigurations; + std::vector> configurations; // Configurations - Asset **assets; // Assets - int nAssets; + std::vector> assets; // Assets }; class POPPLER_PRIVATE_EXPORT Activation @@ -1757,7 +1760,7 @@ class POPPLER_PRIVATE_EXPORT AnnotRichMedia : public Annot // Annots //------------------------------------------------------------------------ -class Annots +class POPPLER_PRIVATE_EXPORT Annots { public: // Build a list of Annot objects and call setPage on them diff --git a/poppler/AnnotStampImageHelper.h b/poppler/AnnotStampImageHelper.h index ebe6e91..8de29d0 100644 --- a/poppler/AnnotStampImageHelper.h +++ b/poppler/AnnotStampImageHelper.h @@ -39,7 +39,7 @@ class POPPLER_PRIVATE_EXPORT AnnotStampImageHelper public: AnnotStampImageHelper(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength); AnnotStampImageHelper(PDFDoc *docA, int widthA, int heightA, ColorSpace colorSpace, int bitsPerComponent, char *data, int dataLength, Ref softMaskRef); - ~AnnotStampImageHelper() { } + ~AnnotStampImageHelper() = default; // Returns the ref to the created Image XObject Ref getRef() const { return ref; } diff --git a/poppler/Array.cc b/poppler/Array.cc index 6f32b3c..c19eea8 100644 --- a/poppler/Array.cc +++ b/poppler/Array.cc @@ -44,7 +44,7 @@ Array::Array(XRef *xrefA) ref = 1; } -Array::~Array() { } +Array::~Array() = default; Array *Array::copy(XRef *xrefA) const { diff --git a/poppler/Array.h b/poppler/Array.h index 50bc9d0..9846bb9 100644 --- a/poppler/Array.h +++ b/poppler/Array.h @@ -16,7 +16,7 @@ // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag -// Copyright (C) 2017-2019, 2021 Albert Astals Cid +// Copyright (C) 2017-2019, 2021, 2024 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018, 2019 Adam Reichold // @@ -89,4 +89,38 @@ class POPPLER_PRIVATE_EXPORT Array mutable std::recursive_mutex mutex; }; +//------------------------------------------------------------------------ +// Object Array accessors. +//------------------------------------------------------------------------ + +inline int Object::arrayGetLength() const +{ + OBJECT_TYPE_CHECK(objArray); + return array->getLength(); +} + +inline void Object::arrayAdd(Object &&elem) +{ + OBJECT_TYPE_CHECK(objArray); + array->add(std::move(elem)); +} + +inline void Object::arrayRemove(int i) +{ + OBJECT_TYPE_CHECK(objArray); + array->remove(i); +} + +inline Object Object::arrayGet(int i, int recursion = 0) const +{ + OBJECT_TYPE_CHECK(objArray); + return array->get(i, recursion); +} + +inline const Object &Object::arrayGetNF(int i) const +{ + OBJECT_TYPE_CHECK(objArray); + return array->getNF(i); +} + #endif diff --git a/poppler/CIDFontsWidthsBuilder.h b/poppler/CIDFontsWidthsBuilder.h new file mode 100644 index 0000000..a7e10ad --- /dev/null +++ b/poppler/CIDFontsWidthsBuilder.h @@ -0,0 +1,196 @@ +//======================================================================== +// +// CIDFontsWidthsBuilder.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2024, 2025 Albert Astals Cid +//======================================================================== + +#ifndef CIDFontsWidthsBuilder_H +#define CIDFontsWidthsBuilder_H + +#include +#include +#include +#include +#include +#include + +/** Class to help build the widths array as defined in + pdf standard 9.7.4.3 Glyph Metrcis in CIDFonts in + ISO 32000-2:2020 + + The way to use this is to create a builder, then add all the widths + and their attached code in order using \ref addWidth and finally call \ref takeSegments + + The resulting value is a list of segments of either \ref ListSegment or + \ref RangeSegment + */ +class CIDFontsWidthsBuilder +{ +public: + /// Segment that should be encoded as a first index and a list of n number specifying the next n widths + class ListSegment + { + public: + int first; + std::vector widths; + }; + /// Segment that should be encoded as 3 integers, first, last (included) and the width for that group. + class RangeSegment + { + public: + int first; + int last; + int width; + }; + using Segment = std::variant; + + /** + * Adds a width for a given index. + * + * Must be called with ever increasing indices until \ref takeSegments + * has been called + */ + void addWidth(int index, int width) + { + if (m_currentSegment.m_lastIndex.has_value() && index <= m_currentSegment.m_lastIndex) { + assert(false); // this is likely a error originating from the user of this code that this function gets called twice with the same or decreasing value. + return; + } + while (!m_currentSegment.accept(index, width)) { + segmentDone(); + } + } + + /** + * \return the resulting segments and resets this font builder + */ + [[nodiscard]] std::vector takeSegments() + { + finish(); + auto rv = std::move(m_segments); + m_segments = {}; + return rv; + } + +private: + void finish() + { + while (!m_currentSegment.m_values.empty()) { + segmentDone(); + } + m_currentSegment = {}; + } + class SegmentBuilder + { + // How many elements at the end has this + int uniqueElementsFromEnd(int value) + { + auto lastDifferent = std::ranges::find_if(std::ranges::reverse_view(m_values), [value](auto &&element) { return element != value; }); + return std::distance(m_values.rbegin(), lastDifferent); + } + + public: + /** Tries to add a index/width combo. + * If a value is not accepted, caller should + * build a segment and repeat the accept call. + * + * \return if accepted or not + */ + bool accept(int index, int value) + { + if (m_lastIndex.has_value() && m_lastIndex != index - 1) { + // we have gaps. That's okay. We just need to ensure to finish the segment + return false; + } + if (!m_firstIndex) { + m_firstIndex = index; + } + if (m_values.size() < 4) { + m_values.push_back(value); + if (m_values.front() != value) { + differentValues = true; + } + m_lastIndex = index; + return true; + } + if (!differentValues) { + if (m_values.back() == value) { + m_values.push_back(value); + m_lastIndex = index; + return true; + } else { + // We need to end a range segment + // to start a new segment with different value + return false; + } + } else { + if (uniqueElementsFromEnd(value) >= 3) { + // We now have at least 3 unique elements + // at the end, so we should finish the previous + // list segment and then start a range segment + return false; + } else { + m_values.push_back(value); + m_lastIndex = index; + return true; + } + } + } + /** + * Builds the segment of the values so far. + */ + Segment build() + { + if (differentValues || m_values.size() < 4) { + std::vector savedValues; + if (m_values.size() >= 4) { + auto lastDifferent = std::ranges::find_if(std::ranges::reverse_view(m_values), [value = m_values.back()](auto &&element) { return element != value; }); + if (std::distance(m_values.rbegin(), lastDifferent) >= 3) { + savedValues.push_back(m_values.back()); + m_values.pop_back(); + while (!m_values.empty() && m_values.back() == savedValues.back()) { + savedValues.push_back(m_values.back()); + m_values.pop_back(); + } + } + } + + assert(m_firstIndex.has_value()); + ListSegment segment { m_firstIndex.value(), std::move(m_values) }; + if (!savedValues.empty()) { + assert(m_lastIndex.has_value()); + m_firstIndex = m_lastIndex.value() - savedValues.size() + 1; + } else { + m_firstIndex = {}; + m_lastIndex = {}; + } + m_values = std::move(savedValues); + differentValues = false; + return segment; + } else { + assert(m_firstIndex.has_value()); + assert(m_lastIndex.has_value()); + auto segment = RangeSegment { m_firstIndex.value(), m_lastIndex.value(), m_values.back() }; + m_values.clear(); + m_firstIndex = {}; + m_lastIndex = {}; + differentValues = false; + return segment; + } + } + std::vector m_values; + std::optional m_lastIndex; + std::optional m_firstIndex; + bool differentValues = false; + }; + std::vector m_segments; + SegmentBuilder m_currentSegment; + + void segmentDone() { m_segments.push_back(m_currentSegment.build()); } +}; + +#endif // CIDFontsWidthsBuilder_H diff --git a/poppler/CMap.cc b/poppler/CMap.cc index 71c357b..e982bbd 100644 --- a/poppler/CMap.cc +++ b/poppler/CMap.cc @@ -14,11 +14,12 @@ // under GPL version 2 or later // // Copyright (C) 2008 Koji Otani -// Copyright (C) 2008, 2009, 2017-2021 Albert Astals Cid +// Copyright (C) 2008, 2009, 2017-2021, 2024 Albert Astals Cid // Copyright (C) 2013 Fabio D'Urso // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 LE GARREC Vincent +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -65,17 +66,15 @@ static int getCharFromStream(void *data) //------------------------------------------------------------------------ -std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA, Object *obj) +std::shared_ptr CMap::parse(CMapCache *cache, const GooString &collectionA, Object *obj) { std::shared_ptr cMap; - GooString *cMapNameA; if (obj->isName()) { - cMapNameA = new GooString(obj->getName()); + const GooString cMapNameA(obj->getName()); if (!(cMap = globalParams->getCMap(collectionA, cMapNameA))) { - error(errSyntaxError, -1, "Unknown CMap '{0:t}' for character collection '{1:t}'", cMapNameA, collectionA); + error(errSyntaxError, -1, "Unknown CMap '{0:t}' for character collection '{1:t}'", &cMapNameA, &collectionA); } - delete cMapNameA; } else if (obj->isStream()) { if (!(cMap = CMap::parse(nullptr, collectionA, obj->getStream()))) { error(errSyntaxError, -1, "Invalid CMap in Type 0 font"); @@ -87,25 +86,25 @@ std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA return cMap; } -std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA, const GooString *cMapNameA) +std::shared_ptr CMap::parse(CMapCache *cache, const GooString &collectionA, const GooString &cMapNameA) { FILE *f; if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) { // Check for an identity CMap. - if (!cMapNameA->cmp("Identity") || !cMapNameA->cmp("Identity-H")) { - return std::shared_ptr(new CMap(collectionA->copy(), cMapNameA->copy(), 0)); + if (!cMapNameA.cmp("Identity") || !cMapNameA.cmp("Identity-H")) { + return std::shared_ptr(new CMap(collectionA.copy(), cMapNameA.copy(), 0)); } - if (!cMapNameA->cmp("Identity-V")) { - return std::shared_ptr(new CMap(collectionA->copy(), cMapNameA->copy(), 1)); + if (!cMapNameA.cmp("Identity-V")) { + return std::shared_ptr(new CMap(collectionA.copy(), cMapNameA.copy(), 1)); } - error(errSyntaxError, -1, "Couldn't find '{0:t}' CMap file for '{1:t}' collection", cMapNameA, collectionA); + error(errSyntaxError, -1, "Couldn't find '{0:t}' CMap file for '{1:t}' collection", &cMapNameA, &collectionA); return {}; } - auto cMap = std::shared_ptr(new CMap(collectionA->copy(), cMapNameA->copy())); + auto cMap = std::shared_ptr(new CMap(collectionA.copy(), cMapNameA.copy())); cMap->parse2(cache, &getCharFromFile, f); fclose(f); @@ -113,16 +112,17 @@ std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA return cMap; } -std::shared_ptr CMap::parse(CMapCache *cache, const GooString *collectionA, Stream *str) +std::shared_ptr CMap::parse(CMapCache *cache, const GooString &collectionA, Stream *str) { - auto cMap = std::shared_ptr(new CMap(collectionA->copy(), nullptr)); + auto cMap = std::shared_ptr(new CMap(collectionA.copy(), nullptr)); Object obj1 = str->getDict()->lookup("UseCMap"); if (!obj1.isNull()) { cMap->useCMap(cache, &obj1); } - str->reset(); - cMap->parse2(cache, &getCharFromStream, str); + if (str->reset()) { + cMap->parse2(cache, &getCharFromStream, str); + } str->close(); return cMap; } @@ -192,25 +192,19 @@ void CMap::parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data) delete pst; } -CMap::CMap(GooString *collectionA, GooString *cMapNameA) +CMap::CMap(std::unique_ptr &&collectionA, std::unique_ptr &&cMapNameA) : collection(std::move(collectionA)), cMapName(std::move(cMapNameA)) { - int i; - - collection = collectionA; - cMapName = cMapNameA; isIdent = false; wMode = 0; vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry)); - for (i = 0; i < 256; ++i) { + for (int i = 0; i < 256; ++i) { vector[i].isVector = false; vector[i].cid = 0; } } -CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) +CMap::CMap(std::unique_ptr &&collectionA, std::unique_ptr &&cMapNameA, int wModeA) : collection(std::move(collectionA)), cMapName(std::move(cMapNameA)) { - collection = collectionA; - cMapName = cMapNameA; isIdent = true; wMode = wModeA; vector = nullptr; @@ -218,20 +212,18 @@ CMap::CMap(GooString *collectionA, GooString *cMapNameA, int wModeA) void CMap::useCMap(CMapCache *cache, const char *useName) { - GooString *useNameStr; std::shared_ptr subCMap; - useNameStr = new GooString(useName); + const GooString useNameStr(useName); // if cache is non-NULL, we already have a lock, and we can use // CMapCache::getCMap() directly; otherwise, we need to use // GlobalParams::getCMap() in order to acqure the lock need to use // GlobalParams::getCMap if (cache) { - subCMap = cache->getCMap(collection, useNameStr); + subCMap = cache->getCMap(*collection, useNameStr); } else { - subCMap = globalParams->getCMap(collection, useNameStr); + subCMap = globalParams->getCMap(*collection, useNameStr); } - delete useNameStr; if (!subCMap) { return; } @@ -243,7 +235,7 @@ void CMap::useCMap(CMapCache *cache, const char *useName) void CMap::useCMap(CMapCache *cache, Object *obj) { - std::shared_ptr subCMap = CMap::parse(cache, collection, obj); + std::shared_ptr subCMap = CMap::parse(cache, *collection, obj); if (!subCMap) { return; } @@ -315,8 +307,6 @@ void CMap::addCIDs(unsigned int start, unsigned int end, unsigned int nBytes, CI CMap::~CMap() { - delete collection; - delete cMapName; if (vector) { freeCMapVector(vector); } @@ -334,9 +324,9 @@ void CMap::freeCMapVector(CMapVectorEntry *vec) gfree(vec); } -bool CMap::match(const GooString *collectionA, const GooString *cMapNameA) +bool CMap::match(const GooString &collectionA, const GooString &cMapNameA) { - return !collection->cmp(collectionA) && !cMapName->cmp(cMapNameA); + return !collection->cmp(collectionA.toStr()) && !cMapName->cmp(cMapNameA.toStr()); } CID CMap::getCID(const char *s, int len, CharCode *c, int *nUsed) @@ -407,9 +397,9 @@ void CMap::setReverseMap(unsigned int *rmap, unsigned int rmapSize, unsigned int //------------------------------------------------------------------------ -CMapCache::CMapCache() { } +CMapCache::CMapCache() = default; -std::shared_ptr CMapCache::getCMap(const GooString *collection, const GooString *cMapName) +std::shared_ptr CMapCache::getCMap(const GooString &collection, const GooString &cMapName) { int i, j; diff --git a/poppler/CMap.h b/poppler/CMap.h index 9ef876e..6a50d3a 100644 --- a/poppler/CMap.h +++ b/poppler/CMap.h @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2008 Koji Otani -// Copyright (C) 2009, 2018-2020, 2022 Albert Astals Cid +// Copyright (C) 2009, 2018-2020, 2022, 2024 Albert Astals Cid // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold // @@ -46,15 +46,15 @@ class CMap public: // Parse a CMap from , which can be a name or a stream. Sets // the initial reference count to 1. Returns NULL on failure. - static std::shared_ptr parse(CMapCache *cache, const GooString *collectionA, Object *obj); + static std::shared_ptr parse(CMapCache *cache, const GooString &collectionA, Object *obj); // Create the CMap specified by and . Sets // the initial reference count to 1. Returns NULL on failure. - static std::shared_ptr parse(CMapCache *cache, const GooString *collectionA, const GooString *cMapNameA); + static std::shared_ptr parse(CMapCache *cache, const GooString &collectionA, const GooString &cMapNameA); // Parse a CMap from . Sets the initial reference count to 1. // Returns NULL on failure. - static std::shared_ptr parse(CMapCache *cache, const GooString *collectionA, Stream *str); + static std::shared_ptr parse(CMapCache *cache, const GooString &collectionA, Stream *str); ~CMap(); @@ -62,13 +62,13 @@ class CMap CMap &operator=(const CMap &) = delete; // Return collection name (-). - const GooString *getCollection() const { return collection; } + const GooString *getCollection() const { return collection.get(); } - const GooString *getCMapName() const { return cMapName; } + const GooString *getCMapName() const { return cMapName.get(); } // Return true if this CMap matches the specified , and // . - bool match(const GooString *collectionA, const GooString *cMapNameA); + bool match(const GooString &collectionA, const GooString &cMapNameA); // Return the CID corresponding to the character code starting at // , which contains bytes. Sets * to the char code, and @@ -82,8 +82,8 @@ class CMap private: void parse2(CMapCache *cache, int (*getCharFunc)(void *), void *data); - CMap(GooString *collectionA, GooString *cMapNameA); - CMap(GooString *collectionA, GooString *cMapNameA, int wModeA); + CMap(std::unique_ptr &&collectionA, std::unique_ptr &&cMapNameA); + CMap(std::unique_ptr &&collectionA, std::unique_ptr &&cMapNameA, int wModeA); void useCMap(CMapCache *cache, const char *useName); void useCMap(CMapCache *cache, Object *obj); void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src); @@ -91,8 +91,8 @@ class CMap void freeCMapVector(CMapVectorEntry *vec); void setReverseMapVector(unsigned int startCode, CMapVectorEntry *vec, unsigned int *rmap, unsigned int rmapSize, unsigned int ncand); - GooString *collection; - GooString *cMapName; + const std::unique_ptr collection; + const std::unique_ptr cMapName; bool isIdent; // true if this CMap is an identity mapping, // or is based on one (via usecmap) int wMode; // writing mode (0=horizontal, 1=vertical) @@ -119,7 +119,7 @@ class CMapCache // Stream is a stream containing the CMap, can be NULL and // this means the CMap will be searched in the CMap files // Returns NULL on failure. - std::shared_ptr getCMap(const GooString *collection, const GooString *cMapName); + std::shared_ptr getCMap(const GooString &collection, const GooString &cMapName); private: std::array, cMapCacheSize> cache; diff --git a/poppler/CachedFile.cc b/poppler/CachedFile.cc index 149a612..e493e95 100644 --- a/poppler/CachedFile.cc +++ b/poppler/CachedFile.cc @@ -6,7 +6,7 @@ // // Copyright 2009 Stefan Thomas // Copyright 2010, 2011 Hib Eris -// Copyright 2010, 2018-2020, 2022 Albert Astals Cid +// Copyright 2010, 2018-2020, 2022, 2024 Albert Astals Cid // Copyright (C) 2013 Julien Nabet // //======================================================================== @@ -124,8 +124,8 @@ int CachedFile::cache(const std::vector &origRanges) int chunk = 0; while (chunk < numChunks) { - while (!chunkNeeded[chunk] && (++chunk != numChunks)) { - ; + while (chunk < numChunks && !chunkNeeded[chunk]) { + chunk++; } if (chunk == numChunks) { break; @@ -133,8 +133,10 @@ int CachedFile::cache(const std::vector &origRanges) startChunk = chunk; loadChunks.push_back(chunk); - while ((++chunk != numChunks) && chunkNeeded[chunk]) { + chunk++; + while (chunk < numChunks && chunkNeeded[chunk]) { loadChunks.push_back(chunk); + chunk++; } endChunk = chunk - 1; @@ -144,12 +146,12 @@ int CachedFile::cache(const std::vector &origRanges) chunk_ranges.push_back(range); } - if (chunk_ranges.size() > 0) { - CachedFileWriter writer = CachedFileWriter(this, &loadChunks); - return loader->load(chunk_ranges, &writer); + if (chunk_ranges.empty()) { + return 0; } - return 0; + CachedFileWriter writer = CachedFileWriter(this, &loadChunks); + return loader->load(chunk_ranges, &writer); } size_t CachedFile::read(void *ptr, size_t unitsize, size_t count) @@ -213,8 +215,6 @@ CachedFileWriter::CachedFileWriter(CachedFile *cachedFileA, std::vector *ch } } -CachedFileWriter::~CachedFileWriter() { } - size_t CachedFileWriter::write(const char *ptr, size_t size) { const char *cp = ptr; diff --git a/poppler/CachedFile.h b/poppler/CachedFile.h index 854692e..3de15ee 100644 --- a/poppler/CachedFile.h +++ b/poppler/CachedFile.h @@ -104,7 +104,7 @@ class POPPLER_PRIVATE_EXPORT CachedFileWriter // The caller is responsible for deleting the cachedFile and chunksA. CachedFileWriter(CachedFile *cachedFile, std::vector *chunksA); - ~CachedFileWriter(); + ~CachedFileWriter() = default; // Writes size bytes from ptr to cachedFile, returns number of bytes written. size_t write(const char *ptr, size_t size); diff --git a/poppler/CairoFontEngine.cc b/poppler/CairoFontEngine.cc index 841e9ef..654694b 100644 --- a/poppler/CairoFontEngine.cc +++ b/poppler/CairoFontEngine.cc @@ -17,11 +17,11 @@ // Copyright (C) 2005-2007 Jeff Muizelaar // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005 Martin Kretzschmar -// Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2005, 2009, 2012, 2013, 2015, 2017-2019, 2021, 2022, 2024, 2025 Albert Astals Cid // Copyright (C) 2006, 2007, 2010, 2011 Carlos Garcia Campos // Copyright (C) 2007 Koji Otani // Copyright (C) 2008, 2009 Chris Wilson -// Copyright (C) 2008, 2012, 2014, 2016, 2017, 2022 Adrian Johnson +// Copyright (C) 2008, 2012, 2014, 2016, 2017, 2022-2024 Adrian Johnson // Copyright (C) 2009 Darren Kenny // Copyright (C) 2010 Suzuki Toshiya // Copyright (C) 2010 Jan Kümmel @@ -33,6 +33,12 @@ // Copyright (C) 2020 Michal // Copyright (C) 2021, 2022 Oliver Sander // Copyright (C) 2022 Marcel Fabian Krüger +// Copyright (C) 2023 Pablo Correa Gómez +// Copyright (C) 2023 Frederic Germain +// Copyright (C) 2023 Ilia Kats +// Copyright (C) 2024 Vincent Lefevre +// Copyright (C) 2024 Dmitry Shubin +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -55,6 +61,8 @@ #include "Gfx.h" #include "Page.h" +#define EMPTY_ARRAY (-1) + //------------------------------------------------------------------------ // CairoFont //------------------------------------------------------------------------ @@ -79,16 +87,19 @@ cairo_font_face_t *CairoFont::getFontFace() return cairo_font_face; } -unsigned long CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen) +std::optional CairoFont::getGlyph(CharCode code, const Unicode *u, int uLen) { FT_UInt gid; if (code < codeToGID.size()) { + if (codeToGID[code] == EMPTY_ARRAY) { + return std::nullopt; + } gid = (FT_UInt)codeToGID[code]; } else { gid = (FT_UInt)code; } - return gid; + return { gid }; } double CairoFont::getSubstitutionCorrection(const std::shared_ptr &gfxFont) @@ -160,10 +171,10 @@ static void _ft_done_face(void *closure) CairoFreeTypeFont::CairoFreeTypeFont(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector &&codeToGIDA, bool substituteA) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), substituteA, true) { } -CairoFreeTypeFont::~CairoFreeTypeFont() { } +CairoFreeTypeFont::~CairoFreeTypeFont() = default; // Create a cairo_font_face_t for the given font filename OR font data. -static std::optional createFreeTypeFontFace(FT_Library lib, const std::string &filename, std::vector &&font_data) +std::optional CairoFreeTypeFont::createFreeTypeFontFace(FT_Library lib, const std::string &filename, std::vector &&font_data) { FreeTypeFontResource *resource = new FreeTypeFontResource; FreeTypeFontFace font_face; @@ -194,22 +205,12 @@ static std::optional createFreeTypeFontFace(FT_Library lib, co return font_face; } -// Create a cairo_font_face_t for the given font filename OR font data. First checks if external font -// is in the cache. -std::optional CairoFreeTypeFont::getFreeTypeFontFace(CairoFontEngine *fontEngine, FT_Library lib, const std::string &filename, std::vector &&font_data) -{ - if (font_data.empty()) { - return fontEngine->getExternalFontFace(lib, filename); - } - - return createFreeTypeFontFace(lib, filename, std::move(font_data)); -} - CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfxFont, XRef *xref, FT_Library lib, CairoFontEngine *fontEngine, bool useCIDs) { std::string fileName; + int faceIndex = 0; std::vector font_data; - int i, n; + int i; std::optional fontLoc; char **enc; const char *name; @@ -224,7 +225,8 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfx GfxFontType fontType = gfxFont->getType(); if (!(fontLoc = gfxFont->locateFont(xref, nullptr))) { - error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); + const std::optional &fontName = gfxFont->getName(); + error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", fontName ? fontName->c_str() : "(unnamed)"); goto err2; } @@ -240,6 +242,7 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfx } else { // gfxFontLocExternal fileName = fontLoc->path; fontType = fontLoc->fontType; + faceIndex = fontLoc->fontNum; substitute = true; } @@ -247,7 +250,7 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfx case fontType1: case fontType1C: case fontType1COT: - font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); + font_face = createFreeTypeFontFace(lib, fileName, std::move(font_data)); if (!font_face) { error(errSyntaxError, -1, "could not create type1 face"); goto err2; @@ -276,51 +279,43 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfx break; case fontCIDType2: case fontCIDType2OT: - if (std::static_pointer_cast(gfxFont)->getCIDToGID()) { - n = std::static_pointer_cast(gfxFont)->getCIDToGIDLen(); - if (n) { - const int *src = std::static_pointer_cast(gfxFont)->getCIDToGID(); - codeToGID.reserve(n); - codeToGID.insert(codeToGID.begin(), src, src + n); - } + if (std::static_pointer_cast(gfxFont)->getCIDToGIDLen() > 0) { + std::vector src = std::static_pointer_cast(gfxFont)->getCIDToGID(); + codeToGID = std::move(src); } else { std::unique_ptr ff; if (!font_data.empty()) { - ff = FoFiTrueType::make(font_data.data(), font_data.size()); + ff = FoFiTrueType::make(font_data.data(), font_data.size(), faceIndex); } else { - ff = FoFiTrueType::load(fileName.c_str()); + ff = FoFiTrueType::load(fileName.c_str(), faceIndex); } if (!ff) { goto err2; } - int *src = std::static_pointer_cast(gfxFont)->getCodeToGIDMap(ff.get(), &n); - codeToGID.reserve(n); - codeToGID.insert(codeToGID.begin(), src, src + n); - gfree(src); + std::vector src = std::static_pointer_cast(gfxFont)->getCodeToGIDMap(ff.get()); + codeToGID = std::move(src); } /* Fall through */ case fontTrueType: case fontTrueTypeOT: { std::unique_ptr ff; if (!font_data.empty()) { - ff = FoFiTrueType::make(font_data.data(), font_data.size()); + ff = FoFiTrueType::make(font_data.data(), font_data.size(), faceIndex); } else { - ff = FoFiTrueType::load(fileName.c_str()); + ff = FoFiTrueType::load(fileName.c_str(), faceIndex); } if (!ff) { - error(errSyntaxError, -1, "failed to load truetype font\n"); + error(errSyntaxError, -1, "failed to load truetype font"); goto err2; } /* This might be set already for the CIDType2 case */ if (fontType == fontTrueType || fontType == fontTrueTypeOT) { - int *src = std::static_pointer_cast(gfxFont)->getCodeToGIDMap(ff.get()); - codeToGID.reserve(256); - codeToGID.insert(codeToGID.begin(), src, src + 256); - gfree(src); + std::vector src = std::static_pointer_cast(gfxFont)->getCodeToGIDMap(ff.get()); + codeToGID = std::move(src); } - font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); + font_face = createFreeTypeFontFace(lib, fileName, std::move(font_data)); if (!font_face) { - error(errSyntaxError, -1, "could not create truetype face\n"); + error(errSyntaxError, -1, "could not create truetype face"); goto err2; } break; @@ -334,52 +329,44 @@ CairoFreeTypeFont *CairoFreeTypeFont::create(const std::shared_ptr &gfx ff1c = FoFiType1C::load(fileName.c_str()); } if (ff1c) { - int *src = ff1c->getCIDToGIDMap(&n); - codeToGID.reserve(n); - codeToGID.insert(codeToGID.begin(), src, src + n); - gfree(src); + std::vector src = ff1c->getCIDToGIDMap(); + codeToGID = std::move(src); delete ff1c; } } - font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); + font_face = createFreeTypeFontFace(lib, fileName, std::move(font_data)); if (!font_face) { - error(errSyntaxError, -1, "could not create cid face\n"); + error(errSyntaxError, -1, "could not create cid face"); goto err2; } break; case fontCIDType0COT: - if (std::static_pointer_cast(gfxFont)->getCIDToGID()) { - n = std::static_pointer_cast(gfxFont)->getCIDToGIDLen(); - if (n) { - const int *src = std::static_pointer_cast(gfxFont)->getCIDToGID(); - codeToGID.reserve(n); - codeToGID.insert(codeToGID.begin(), src, src + n); - } + if (std::static_pointer_cast(gfxFont)->getCIDToGIDLen() > 0) { + std::vector src = std::static_pointer_cast(gfxFont)->getCIDToGID(); + codeToGID = std::move(src); } if (codeToGID.empty()) { if (!useCIDs) { std::unique_ptr ff; if (!font_data.empty()) { - ff = FoFiTrueType::make(font_data.data(), font_data.size()); + ff = FoFiTrueType::make(font_data.data(), font_data.size(), faceIndex); } else { - ff = FoFiTrueType::load(fileName.c_str()); + ff = FoFiTrueType::load(fileName.c_str(), faceIndex); } if (ff) { if (ff->isOpenTypeCFF()) { - int *src = ff->getCIDToGIDMap(&n); - codeToGID.reserve(n); - codeToGID.insert(codeToGID.begin(), src, src + n); - gfree(src); + std::vector src = ff->getCIDToGIDMap(); + codeToGID = std::move(src); } } } } - font_face = getFreeTypeFontFace(fontEngine, lib, fileName, std::move(font_data)); + font_face = createFreeTypeFontFace(lib, fileName, std::move(font_data)); if (!font_face) { - error(errSyntaxError, -1, "could not create cid (OT) face\n"); + error(errSyntaxError, -1, "could not create cid (OT) face"); goto err2; } break; @@ -405,18 +392,20 @@ static const cairo_user_data_key_t type3_font_key = { 0 }; typedef struct _type3_font_info { - _type3_font_info(const std::shared_ptr &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, bool printingA, XRef *xrefA) : font(fontA), doc(docA), fontEngine(fontEngineA), printing(printingA), xref(xrefA) { } + _type3_font_info(const std::shared_ptr &fontA, PDFDoc *docA, CairoFontEngine *fontEngineA, CairoOutputDev *outputDevA, Gfx *gfxA) : font(fontA), doc(docA), fontEngine(fontEngineA), outputDev(outputDevA), gfx(gfxA) { } std::shared_ptr font; PDFDoc *doc; CairoFontEngine *fontEngine; - bool printing; - XRef *xref; + CairoOutputDev *outputDev; + Gfx *gfx; } type3_font_info_t; static void _free_type3_font_info(void *closure) { type3_font_info_t *info = (type3_font_info_t *)closure; + delete info->gfx; + delete info->outputDev; delete info; } @@ -439,24 +428,24 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi { Dict *charProcs; Object charProc; - CairoOutputDev *output_dev; cairo_matrix_t matrix, invert_y_axis; const double *mat; double wx, wy; - PDFRectangle box; type3_font_info_t *info; + Gfx *gfx; cairo_status_t status; info = (type3_font_info_t *)cairo_font_face_get_user_data(cairo_scaled_font_get_font_face(scaled_font), &type3_font_key); - Dict *resDict = std::static_pointer_cast(info->font)->getResources(); charProcs = std::static_pointer_cast(info->font)->getCharProcs(); if (!charProcs) { - return CAIRO_STATUS_USER_FONT_ERROR; + // error already logged by Gfx8BitFont constructor + return CAIRO_STATUS_SUCCESS; } - if ((int)glyph >= charProcs->getLength()) { - return CAIRO_STATUS_USER_FONT_ERROR; + if (glyph >= (unsigned)charProcs->getLength()) { + error(errSyntaxError, -1, "Type3 character not in /CharProcs"); + return CAIRO_STATUS_SUCCESS; } mat = info->font->getFontMatrix(); @@ -470,22 +459,23 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi cairo_matrix_multiply(&matrix, &matrix, &invert_y_axis); cairo_transform(cr, &matrix); - output_dev = new CairoOutputDev(); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_set_source(cr, cairo_user_scaled_font_get_foreground_marker(scaled_font)); +#endif + + CairoOutputDev *output_dev = info->outputDev; output_dev->setCairo(cr); - output_dev->setPrinting(info->printing); - mat = info->font->getFontBBox(); - box.x1 = mat[0]; - box.y1 = mat[1]; - box.x2 = mat[2]; - box.y2 = mat[3]; - auto gfx = std::make_unique(info->doc, output_dev, resDict, &box, nullptr); + gfx = info->gfx; + gfx->saveState(); + output_dev->startDoc(info->doc, info->fontEngine); output_dev->startType3Render(gfx->getState(), gfx->getXRef()); - output_dev->setInType3Char(true); + output_dev->setType3RenderType(color ? CairoOutputDev::Type3RenderColor : CairoOutputDev::Type3RenderMask); charProc = charProcs->getVal(glyph); if (!charProc.isStream()) { - return CAIRO_STATUS_USER_FONT_ERROR; + error(errSyntaxError, -1, "Type3 character /{0:s} value not a stream", charProcs->getKey(glyph)); + return CAIRO_STATUS_SUCCESS; } Object charProcResObject = charProc.streamGetDict()->lookup("Resources"); if (charProcResObject.isDict()) { @@ -510,6 +500,7 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi metrics->width = bbox[2] - bbox[0]; metrics->height = bbox[3] - bbox[1]; } + gfx->restoreState(); status = CAIRO_STATUS_SUCCESS; @@ -520,12 +511,10 @@ static cairo_status_t _render_type3_glyph(cairo_scaled_font_t *scaled_font, unsi status = CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED; } - delete output_dev; - return status; } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 6) +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) static cairo_status_t _render_type3_color_glyph(cairo_scaled_font_t *scaled_font, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *metrics) { return _render_type3_glyph(scaled_font, glyph, cr, metrics, true); @@ -541,26 +530,39 @@ CairoType3Font *CairoType3Font::create(const std::shared_ptr &gfxFont, { std::vector codeToGID; char *name; + const double *mat; Dict *charProcs = std::static_pointer_cast(gfxFont)->getCharProcs(); Ref ref = *gfxFont->getID(); cairo_font_face_t *font_face = cairo_user_font_face_create(); cairo_user_font_face_set_init_func(font_face, _init_type3_glyph); -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 6) +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) // When both callbacks are set, Cairo will call the color glyph // callback first. If that returns NOT_IMPLEMENTED, Cairo will // then call the non-color glyph callback. cairo_user_font_face_set_render_color_glyph_func(font_face, _render_type3_color_glyph); #endif cairo_user_font_face_set_render_glyph_func(font_face, _render_type3_noncolor_glyph); - type3_font_info_t *info = new type3_font_info_t(gfxFont, doc, fontEngine, printing, xref); + CairoOutputDev *output_dev = new CairoOutputDev(); + output_dev->setPrinting(printing); + + Dict *resDict = std::static_pointer_cast(gfxFont)->getResources(); + mat = gfxFont->getFontBBox(); + PDFRectangle box; + box.x1 = mat[0]; + box.y1 = mat[1]; + box.x2 = mat[2]; + box.y2 = mat[3]; + Gfx *gfx = new Gfx(doc, output_dev, resDict, &box, nullptr); + + type3_font_info_t *info = new type3_font_info_t(gfxFont, doc, fontEngine, output_dev, gfx); cairo_font_face_set_user_data(font_face, &type3_font_key, (void *)info, _free_type3_font_info); char **enc = std::static_pointer_cast(gfxFont)->getEncoding(); codeToGID.resize(256); for (int i = 0; i < 256; ++i) { - codeToGID[i] = 0; + codeToGID[i] = EMPTY_ARRAY; if (charProcs && (name = enc[i])) { for (int j = 0; j < charProcs->getLength(); j++) { if (strcmp(name, charProcs->getKey(j)) == 0) { @@ -575,7 +577,7 @@ CairoType3Font *CairoType3Font::create(const std::shared_ptr &gfxFont, CairoType3Font::CairoType3Font(Ref refA, cairo_font_face_t *cairo_font_faceA, std::vector &&codeToGIDA, bool printingA, XRef *xref) : CairoFont(refA, cairo_font_faceA, std::move(codeToGIDA), false, printingA) { } -CairoType3Font::~CairoType3Font() { } +CairoType3Font::~CairoType3Font() = default; bool CairoType3Font::matches(Ref &other, bool printingA) { @@ -586,9 +588,6 @@ bool CairoType3Font::matches(Ref &other, bool printingA) // CairoFontEngine //------------------------------------------------------------------------ -std::unordered_map CairoFontEngine::fontFileCache; -std::recursive_mutex CairoFontEngine::fontFileCacheMutex; - CairoFontEngine::CairoFontEngine(FT_Library libA) { lib = libA; @@ -600,7 +599,7 @@ CairoFontEngine::CairoFontEngine(FT_Library libA) useCIDs = major > 2 || (major == 2 && (minor > 1 || (minor == 1 && patch > 7))); } -CairoFontEngine::~CairoFontEngine() { } +CairoFontEngine::~CairoFontEngine() = default; std::shared_ptr CairoFontEngine::getFont(const std::shared_ptr &gfxFont, PDFDoc *doc, bool printing, XRef *xref) { @@ -637,33 +636,3 @@ std::shared_ptr CairoFontEngine::getFont(const std::shared_ptr CairoFontEngine::getExternalFontFace(FT_Library ftlib, const std::string &filename) -{ - std::scoped_lock lock(fontFileCacheMutex); - - auto it = fontFileCache.find(filename); - if (it != fontFileCache.end()) { - FreeTypeFontFace font = it->second; - cairo_font_face_reference(font.cairo_font_face); - return font; - } - - std::optional font_face = createFreeTypeFontFace(ftlib, filename, {}); - if (font_face) { - cairo_font_face_reference(font_face->cairo_font_face); - fontFileCache[filename] = *font_face; - } - - it = fontFileCache.begin(); - while (it != fontFileCache.end()) { - if (cairo_font_face_get_reference_count(it->second.cairo_font_face) == 1) { - cairo_font_face_destroy(it->second.cairo_font_face); - it = fontFileCache.erase(it); - } else { - ++it; - } - } - - return font_face; -} diff --git a/poppler/CairoFontEngine.h b/poppler/CairoFontEngine.h index c97e0ea..a26f826 100644 --- a/poppler/CairoFontEngine.h +++ b/poppler/CairoFontEngine.h @@ -18,7 +18,7 @@ // Copyright (C) 2005, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2006, 2010 Carlos Garcia Campos -// Copyright (C) 2008, 2017, 2022 Adrian Johnson +// Copyright (C) 2008, 2017, 2022, 2024 Adrian Johnson // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2018 Adam Reichold // Copyright (C) 2022 Oliver Sander @@ -55,7 +55,7 @@ class CairoFont virtual bool matches(Ref &other, bool printing); cairo_font_face_t *getFontFace(); - unsigned long getGlyph(CharCode code, const Unicode *u, int uLen); + std::optional getGlyph(CharCode code, const Unicode *u, int uLen); double getSubstitutionCorrection(const std::shared_ptr &gfxFont); bool isSubstitute() { return substitute; } @@ -89,7 +89,7 @@ class CairoFreeTypeFont : public CairoFont private: CairoFreeTypeFont(Ref ref, cairo_font_face_t *cairo_font_face, std::vector &&codeToGID, bool substitute); - static std::optional getFreeTypeFontFace(CairoFontEngine *fontEngine, FT_Library lib, const std::string &filename, std::vector &&data); + static std::optional createFreeTypeFontFace(FT_Library lib, const std::string &filename, std::vector &&font_data); }; //------------------------------------------------------------------------ @@ -123,8 +123,6 @@ class CairoFontEngine std::shared_ptr getFont(const std::shared_ptr &gfxFont, PDFDoc *doc, bool printing, XRef *xref); - static std::optional getExternalFontFace(FT_Library ftlib, const std::string &filename); - private: FT_Library lib; bool useCIDs; @@ -134,10 +132,6 @@ class CairoFontEngine // Most recently used is at the end of the vector. static const size_t cairoFontCacheSize = 64; std::vector> fontCache; - - // Global cache of cairo_font_face_t for external font files. - static std::unordered_map fontFileCache; - static std::recursive_mutex fontFileCacheMutex; }; #endif diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc index a72c218..8a1d5f8 100644 --- a/poppler/CairoOutputDev.cc +++ b/poppler/CairoOutputDev.cc @@ -16,11 +16,11 @@ // // Copyright (C) 2005-2008 Jeff Muizelaar // Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005, 2009, 2012, 2017-2021 Albert Astals Cid +// Copyright (C) 2005, 2009, 2012, 2017-2021, 2023, 2024 Albert Astals Cid // Copyright (C) 2005 Nickolay V. Shmyrev // Copyright (C) 2006-2011, 2013, 2014, 2017, 2018 Carlos Garcia Campos // Copyright (C) 2008 Carl Worth -// Copyright (C) 2008-2018, 2021, 2022 Adrian Johnson +// Copyright (C) 2008-2018, 2021-2024 Adrian Johnson // Copyright (C) 2008 Michael Vrable // Copyright (C) 2008, 2009 Chris Wilson // Copyright (C) 2008, 2012 Hib Eris @@ -36,6 +36,13 @@ // Copyright (C) 2020, 2022 Oliver Sander // Copyright (C) 2021 Uli Schlachter // Copyright (C) 2021 Christian Persch +// Copyright (C) 2022 Zachary Travis +// Copyright (C) 2023 Artemy Gordon +// Copyright (C) 2023 Anton Thomasson +// Copyright (C) 2024 Vincent Lefevre +// Copyright (C) 2024 Athul Raj Kollareth +// Copyright (C) 2024, 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -67,6 +74,7 @@ #include "CairoFontEngine.h" #include "CairoRescaleBox.h" #include "UnicodeMap.h" +#include "UTF.h" #include "JBIG2Stream.h" //------------------------------------------------------------------------ @@ -76,6 +84,9 @@ // cairo images to this size. 8192 is sufficient for an A2 sized // 300ppi image. #define MAX_PRINT_IMAGE_SIZE 8192 +// Cairo has a max size for image surfaces due to their fixed-point +// coordinate handling, namely INT16_MAX, aka 32767. +#define MAX_CAIRO_IMAGE_SIZE 32767 #ifdef LOG_CAIRO # define LOG(x) (x) @@ -146,15 +157,10 @@ CairoOutputDev::CairoOutputDev() strokePathClip = nullptr; cairo = nullptr; currentFont = nullptr; -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) - prescaleImages = false; -#else - prescaleImages = true; -#endif printing = true; use_show_text_glyphs = false; inUncoloredPattern = false; - inType3Char = false; + t3_render_state = Type3RenderNone; t3_glyph_has_bbox = false; t3_glyph_has_color = false; text_matrix_valid = true; @@ -168,6 +174,9 @@ CairoOutputDev::CairoOutputDev() textPage = nullptr; actualText = nullptr; + logicalStruct = false; + pdfPageNum = 0; + cairoPageNum = 0; // the SA parameter supposedly defaults to false, but Acrobat // apparently hardwires it to true @@ -175,6 +184,7 @@ CairoOutputDev::CairoOutputDev() align_stroke_coords = false; adjusted_stroke_width = false; xref = nullptr; + currentStructParents = -1; } CairoOutputDev::~CairoOutputDev() @@ -204,9 +214,7 @@ CairoOutputDev::~CairoOutputDev() if (textPage) { textPage->decRefCnt(); } - if (actualText) { - delete actualText; - } + delete actualText; } void CairoOutputDev::setCairo(cairo_t *c) @@ -214,7 +222,7 @@ void CairoOutputDev::setCairo(cairo_t *c) if (cairo != nullptr) { cairo_status_t status = cairo_status(cairo); if (status) { - error(errInternal, -1, "cairo context error: {0:s}\n", cairo_status_to_string(status)); + error(errInternal, -1, "cairo context error: {0:s}", cairo_status_to_string(status)); } cairo_destroy(cairo); assert(!cairo_shape); @@ -230,14 +238,20 @@ void CairoOutputDev::setCairo(cairo_t *c) } } +bool CairoOutputDev::isPDF() +{ + if (cairo) { + return cairo_surface_get_type(cairo_get_target(cairo)) == CAIRO_SURFACE_TYPE_PDF; + } + return false; +} + void CairoOutputDev::setTextPage(TextPage *text) { if (textPage) { textPage->decRefCnt(); } - if (actualText) { - delete actualText; - } + delete actualText; if (text) { textPage = text; textPage->incRefCnt(); @@ -264,17 +278,71 @@ void CairoOutputDev::startDoc(PDFDoc *docA, CairoFontEngine *parentFontEngine) if (parentFontEngine) { fontEngine = parentFontEngine; } else { - if (fontEngine) { - delete fontEngine; - } + delete fontEngine; fontEngine = new CairoFontEngine(ft_lib); fontEngine_owner = true; } xref = doc->getXRef(); + + mcidEmitted.clear(); + destsMap.clear(); + emittedDestinations.clear(); + pdfPageToCairoPageMap.clear(); + pdfPageRefToCairoPageNumMap.clear(); + cairoPageNum = 0; + firstPage = true; +} + +void CairoOutputDev::textStringToQuotedUtf8(const GooString *text, GooString *s) +{ + std::string utf8 = TextStringToUtf8(text->toStr()); + s->Set("'"); + for (char c : utf8) { + if (c == '\\' || c == '\'') { + s->append("\\"); + } + s->append(c); + } + s->append("'"); +} + +// Initialization that needs to be performed after setCairo() is called. +void CairoOutputDev::startFirstPage(int pageNum, GfxState *state, XRef *xrefA) +{ + if (xrefA) { + xref = xrefA; + } + + if (logicalStruct && isPDF()) { + int numDests = doc->getCatalog()->numDestNameTree(); + for (int i = 0; i < numDests; i++) { + const GooString *name = doc->getCatalog()->getDestNameTreeName(i); + std::unique_ptr dest = doc->getCatalog()->getDestNameTreeDest(i); + if (dest->isPageRef()) { + Ref ref = dest->getPageRef(); + destsMap[ref].insert({ std::string(name->toStr()), std::move(dest) }); + } + } + + numDests = doc->getCatalog()->numDests(); + for (int i = 0; i < numDests; i++) { + const char *name = doc->getCatalog()->getDestsName(i); + std::unique_ptr dest = doc->getCatalog()->getDestsDest(i); + if (dest->isPageRef()) { + Ref ref = dest->getPageRef(); + destsMap[ref].insert({ std::string(name), std::move(dest) }); + } + } + } } void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { + if (firstPage) { + startFirstPage(pageNum, state, xrefA); + firstPage = false; + } + /* set up some per page defaults */ cairo_pattern_destroy(fill_pattern); cairo_pattern_destroy(stroke_pattern); @@ -287,8 +355,52 @@ void CairoOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) if (textPage) { textPage->startPage(state); } - if (xrefA != nullptr) { - xref = xrefA; + + pdfPageNum = pageNum; + cairoPageNum++; + pdfPageToCairoPageMap[pdfPageNum] = cairoPageNum; + + if (logicalStruct && isPDF()) { + Object obj = doc->getPage(pageNum)->getAnnotsObject(xref); + Annots *annots = new Annots(doc, pageNum, &obj); + + for (Annot *annot : annots->getAnnots()) { + if (annot->getType() == Annot::typeLink) { + annot->incRefCnt(); + annotations.push_back(annot); + } + } + + delete annots; + + // emit dests + Ref *ref = doc->getCatalog()->getPageRef(pageNum); + pdfPageRefToCairoPageNumMap[*ref] = cairoPageNum; + auto pageDests = destsMap.find(*ref); + if (pageDests != destsMap.end()) { + for (auto &it : pageDests->second) { + GooString quoted_name; + GooString name(it.first); + textStringToQuotedUtf8(&name, "ed_name); + emittedDestinations.insert(quoted_name.toStr()); + + GooString attrib; + attrib.appendf("name={0:t} ", "ed_name); + if (it.second->getChangeLeft()) { + attrib.appendf("x={0:g} ", it.second->getLeft()); + } + if (it.second->getChangeTop()) { + attrib.appendf("y={0:g} ", state->getPageHeight() - it.second->getTop()); + } + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_tag_begin(cairo, CAIRO_TAG_DEST, attrib.c_str()); + cairo_tag_end(cairo, CAIRO_TAG_DEST); +#endif + } + } + + currentStructParents = doc->getPage(pageNum)->getStructParents(); } } @@ -300,6 +412,309 @@ void CairoOutputDev::endPage() } } +void CairoOutputDev::beginForm(Object *obj, Ref id) +{ + if (logicalStruct && isPDF()) { + structParentsStack.push_back(currentStructParents); + + const Object tmp = obj->streamGetDict()->lookup("StructParents"); + if (!(tmp.isInt() || tmp.isNull())) { + error(errSyntaxError, -1, "XObject StructParents object is wrong type ({0:s})", tmp.getTypeName()); + } else if (tmp.isInt()) { + currentStructParents = tmp.getInt(); + } + } +} + +void CairoOutputDev::endForm(Object *obj, Ref id) +{ + if (logicalStruct && isPDF()) { + currentStructParents = structParentsStack.back(); + structParentsStack.pop_back(); + } +} + +void CairoOutputDev::quadToCairoRect(AnnotQuadrilaterals *quads, int idx, double pageHeight, cairo_rectangle_t *rect) +{ + double x1, x2, y1, y2; + x1 = x2 = quads->getX1(idx); + y1 = y2 = quads->getX2(idx); + + x1 = std::min(x1, quads->getX2(idx)); + x1 = std::min(x1, quads->getX3(idx)); + x1 = std::min(x1, quads->getX4(idx)); + + y1 = std::min(y1, quads->getY2(idx)); + y1 = std::min(y1, quads->getY3(idx)); + y1 = std::min(y1, quads->getY4(idx)); + + x2 = std::max(x2, quads->getX2(idx)); + x2 = std::max(x2, quads->getX3(idx)); + x2 = std::max(x2, quads->getX4(idx)); + + y2 = std::max(y2, quads->getY2(idx)); + y2 = std::max(y2, quads->getY3(idx)); + y2 = std::max(y2, quads->getY4(idx)); + + rect->x = x1; + rect->y = pageHeight - y2; + rect->width = x2 - x1; + rect->height = y2 - y1; +} + +bool CairoOutputDev::appendLinkDestRef(GooString *s, const LinkDest *dest) +{ + Ref ref = dest->getPageRef(); + auto pageNum = pdfPageRefToCairoPageNumMap.find(ref); + if (pageNum != pdfPageRefToCairoPageNumMap.end()) { + auto cairoPage = pdfPageToCairoPageMap.find(pageNum->second); + if (cairoPage != pdfPageToCairoPageMap.end()) { + s->appendf("page={0:d} ", cairoPage->second); + double destPageHeight = doc->getPageMediaHeight(dest->getPageNum()); + appendLinkDestXY(s, dest, destPageHeight); + return true; + } + } + return false; +} + +void CairoOutputDev::appendLinkDestXY(GooString *s, const LinkDest *dest, double destPageHeight) +{ + double x = 0; + double y = 0; + + if (dest->getChangeLeft()) { + x = dest->getLeft(); + } + + if (dest->getChangeTop()) { + y = dest->getTop(); + } + + // if pageHeight is 0, dest is remote document, cairo uses PDF coords in this + // case. So don't flip coords when pageHeight is 0. + s->appendf("pos=[{0:g} {1:g}] ", x, destPageHeight ? destPageHeight - y : y); +} + +bool CairoOutputDev::beginLinkTag(AnnotLink *annotLink) +{ + int page_num = annotLink->getPageNum(); + double height = doc->getPageMediaHeight(page_num); + + GooString attrib; + attrib.appendf("link_page={0:d} ", page_num); + attrib.append("rect=["); + AnnotQuadrilaterals *quads = annotLink->getQuadrilaterals(); + if (quads && quads->getQuadrilateralsLength() > 0) { + for (int i = 0; i < quads->getQuadrilateralsLength(); i++) { + cairo_rectangle_t rect; + quadToCairoRect(quads, i, height, &rect); + attrib.appendf("{0:g} {1:g} {2:g} {3:g} ", rect.x, rect.y, rect.width, rect.height); + } + } else { + double x1, x2, y1, y2; + annotLink->getRect(&x1, &y1, &x2, &y2); + attrib.appendf("{0:g} {1:g} {2:g} {3:g} ", x1, height - y2, x2 - x1, y2 - y1); + } + attrib.append("] "); + + LinkAction *action = annotLink->getAction(); + if (action->getKind() == actionGoTo) { + LinkGoTo *act = static_cast(action); + if (act->isOk()) { + const GooString *namedDest = act->getNamedDest(); + const LinkDest *linkDest = act->getDest(); + if (namedDest) { + GooString name; + textStringToQuotedUtf8(namedDest, &name); + if (!emittedDestinations.contains(name.toStr())) { + return false; + } + attrib.appendf("dest={0:t} ", &name); + } else if (linkDest && linkDest->isOk() && linkDest->isPageRef()) { + bool ok = appendLinkDestRef(&attrib, linkDest); + if (!ok) { + return false; + } + } + } + } else if (action->getKind() == actionGoToR) { + LinkGoToR *act = static_cast(action); + attrib.appendf("file='{0:t}' ", act->getFileName()); + const GooString *namedDest = act->getNamedDest(); + const LinkDest *linkDest = act->getDest(); + if (namedDest) { + GooString name; + textStringToQuotedUtf8(namedDest, &name); + if (!emittedDestinations.contains(name.toStr())) { + return false; + } + attrib.appendf("dest={0:t} ", &name); + } else if (linkDest && linkDest->isOk() && !linkDest->isPageRef()) { + auto cairoPage = pdfPageToCairoPageMap.find(linkDest->getPageNum()); + if (cairoPage != pdfPageToCairoPageMap.end()) { + attrib.appendf("page={0:d} ", cairoPage->second); + appendLinkDestXY(&attrib, linkDest, 0.0); + } else { + return false; + } + } + } else if (action->getKind() == actionURI) { + LinkURI *act = static_cast(action); + if (act->isOk()) { + attrib.appendf("uri='{0:s}'", act->getURI().c_str()); + } + } +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_tag_begin(cairo, CAIRO_TAG_LINK, attrib.c_str()); +#endif + return true; +} + +AnnotLink *CairoOutputDev::findLinkObject(const StructElement *elem) +{ + if (elem->isObjectRef()) { + Ref ref = elem->getObjectRef(); + for (Annot *annot : annotations) { + if (annot->getType() == Annot::typeLink && annot->match(&ref)) { + return static_cast(annot); + } + } + } + + for (unsigned i = 0; i < elem->getNumChildren(); i++) { + AnnotLink *link = findLinkObject(elem->getChild(i)); + if (link) { + return link; + } + } + + return nullptr; +} + +bool CairoOutputDev::beginLink(const StructElement *linkElem) +{ + bool emitted = true; + AnnotLink *linkAnnot = findLinkObject(linkElem); + if (linkAnnot) { + emitted = beginLinkTag(linkAnnot); + } else { +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_tag_begin(cairo, linkElem->getTypeName(), nullptr); +#endif + } + return emitted; +} + +void CairoOutputDev::getStructElemAttributeString(const StructElement *elem) +{ + int mcid = 0; + GooString attribs; + Ref ref = elem->getObjectRef(); + attribs.appendf("id='{0:d}_{1:d}_{2:d}'", ref.num, ref.gen, mcid); + attribs.appendf(" parent='{0:d}_{1:d}'", ref.num, ref.gen); +} + +int CairoOutputDev::getContentElementStructParents(const StructElement *element) +{ + int structParents = -1; + Ref ref; + + if (element->hasStmRef()) { + element->getStmRef(ref); + Object xobjectObj = xref->fetch(ref); + const Object &spObj = xobjectObj.streamGetDict()->lookup("StructParents"); + if (spObj.isInt()) { + structParents = spObj.getInt(); + } + } else if (element->hasPageRef()) { + element->getPageRef(ref); + Object pageObj = xref->fetch(ref); + const Object &spObj = pageObj.dictLookup("StructParents"); + if (spObj.isInt()) { + structParents = spObj.getInt(); + } + } + + if (structParents == -1) { + error(errSyntaxError, -1, "Unable to find StructParents object for StructElement"); + } + return structParents; +} + +bool CairoOutputDev::checkIfStructElementNeeded(const StructElement *element) +{ + if (element->isContent() && !element->isObjectRef()) { + int structParents = getContentElementStructParents(element); + int mcid = element->getMCID(); + if (mcidEmitted.contains(std::pair(structParents, mcid))) { + structElementNeeded.insert(element); + return true; + } + } else if (!element->isContent()) { + bool needed = false; + for (unsigned i = 0; i < element->getNumChildren(); i++) { + if (checkIfStructElementNeeded(element->getChild(i))) { + needed = true; + } + } + if (needed) { + structElementNeeded.insert(element); + } + return needed; + } + return false; +} + +void CairoOutputDev::emitStructElement(const StructElement *element) +{ + if (!structElementNeeded.contains(element)) { + return; + } + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + if (element->isContent() && !element->isObjectRef()) { + int structParents = getContentElementStructParents(element); + int mcid = element->getMCID(); + GooString attribs; + attribs.appendf("ref='{0:d}_{1:d}'", structParents, mcid); + cairo_tag_begin(cairo, CAIRO_TAG_CONTENT_REF, attribs.c_str()); + cairo_tag_end(cairo, CAIRO_TAG_CONTENT_REF); + } else if (!element->isContent()) { + if (element->getType() == StructElement::Link) { + bool ok = beginLink(element); + if (!ok) { + return; + } + } else { + cairo_tag_begin(cairo, element->getTypeName(), ""); + } + for (unsigned i = 0; i < element->getNumChildren(); i++) { + emitStructElement(element->getChild(i)); + } + cairo_tag_end(cairo, element->getTypeName()); + } +#endif +} + +void CairoOutputDev::emitStructTree() +{ + if (logicalStruct && isPDF()) { + const StructTreeRoot *root = doc->getStructTreeRoot(); + if (!root) { + return; + } + + for (unsigned i = 0; i < root->getNumChildren(); i++) { + checkIfStructElementNeeded(root->getChild(i)); + } + + for (unsigned i = 0; i < root->getNumChildren(); i++) { + emitStructElement(root->getChild(i)); + } + } +} + void CairoOutputDev::startType3Render(GfxState *state, XRef *xrefA) { /* When cairo calls a user font render function, the default @@ -468,7 +883,7 @@ void CairoOutputDev::updateCTM(GfxState *state, double m11, double m12, double m * instead of having to invert the matrix. */ invert_matrix = matrix; if (cairo_matrix_invert(&invert_matrix)) { - error(errSyntaxWarning, -1, "matrix not invertible\n"); + error(errSyntaxWarning, -1, "matrix not invertible"); return; } @@ -501,13 +916,13 @@ void CairoOutputDev::updateFlatness(GfxState *state) void CairoOutputDev::updateLineJoin(GfxState *state) { switch (state->getLineJoin()) { - case 0: + case GfxState::LineJoinMitre: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_MITER); break; - case 1: + case GfxState::LineJoinRound: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_ROUND); break; - case 2: + case GfxState::LineJoinBevel: cairo_set_line_join(cairo, CAIRO_LINE_JOIN_BEVEL); break; } @@ -519,13 +934,13 @@ void CairoOutputDev::updateLineJoin(GfxState *state) void CairoOutputDev::updateLineCap(GfxState *state) { switch (state->getLineCap()) { - case 0: + case GfxState::LineCapButt: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_BUTT); break; - case 1: + case GfxState::LineCapRound: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND); break; - case 2: + case GfxState::LineCapProjecting: cairo_set_line_cap(cairo, CAIRO_LINE_CAP_SQUARE); break; } @@ -887,7 +1302,7 @@ void CairoOutputDev::doPath(cairo_t *c, GfxState *state, const GfxPath *path) void CairoOutputDev::stroke(GfxState *state) { - if (inType3Char) { + if (t3_render_state == Type3RenderMask) { GfxGray gray; state->getFillGray(&gray); if (colToDbl(gray) > 0.5) { @@ -918,7 +1333,7 @@ void CairoOutputDev::stroke(GfxState *state) void CairoOutputDev::fill(GfxState *state) { - if (inType3Char) { + if (t3_render_state == Type3RenderMask) { GfxGray gray; state->getFillGray(&gray); if (colToDbl(gray) > 0.5) { @@ -1105,7 +1520,6 @@ bool CairoOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat, return true; } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) bool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) { // Function shaded fills are subdivided to rectangles that are the @@ -1113,6 +1527,10 @@ bool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *sha // in points. const int subdivide_pixels = 10; + // Set a minimum step to force upon {x|y}_step, to avoid approximate or reach + // infinite loop when {x|y}_step approximates to or equals zero - Issue #1520 + const double minimum_step = 0.01; + double x_begin, x_end, x1, x2; double y_begin, y_end, y1, y2; double x_step; @@ -1129,7 +1547,7 @@ bool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *sha mat.x0 = matrix[4]; mat.y0 = matrix[5]; if (cairo_matrix_invert(&mat)) { - error(errSyntaxWarning, -1, "matrix not invertible\n"); + error(errSyntaxWarning, -1, "matrix not invertible"); return false; } @@ -1137,6 +1555,13 @@ bool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *sha x_step = y_step = subdivide_pixels; cairo_matrix_transform_distance(&mat, &x_step, &y_step); + if (y_step < minimum_step) { + y_step = minimum_step; + } + if (x_step < minimum_step) { + x_step = minimum_step; + } + cairo_pattern_destroy(fill_pattern); fill_pattern = cairo_pattern_create_mesh(); cairo_pattern_set_matrix(fill_pattern, &mat); @@ -1193,7 +1618,6 @@ bool CairoOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *sha return true; } -#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ bool CairoOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) { @@ -1264,7 +1688,6 @@ bool CairoOutputDev::radialShadedSupportExtend(GfxState *state, GfxRadialShading return (shading->getExtend0() == shading->getExtend1()); } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) bool CairoOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) { double x0, y0, x1, y1, x2, y2; @@ -1392,7 +1815,6 @@ bool CairoOutputDev::patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *s return true; } -#endif /* CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) */ void CairoOutputDev::clip(GfxState *state) { @@ -1481,11 +1903,16 @@ void CairoOutputDev::beginString(GfxState *state, const GooString *s) void CairoOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { + std::optional glyphIndex; + if (currentFont) { - glyphs[glyphCount].index = currentFont->getGlyph(code, u, uLen); - glyphs[glyphCount].x = x - originX; - glyphs[glyphCount].y = y - originY; - glyphCount++; + glyphIndex = currentFont->getGlyph(code, u, uLen); + if (glyphIndex) { + glyphs[glyphCount].index = *glyphIndex; + glyphs[glyphCount].x = x - originX; + glyphs[glyphCount].y = y - originY; + glyphCount++; + } if (use_show_text_glyphs) { const UnicodeMap *utf8Map = globalParams->getUtf8Map(); if (utf8Max - utf8Count < uLen * 6) { @@ -1536,6 +1963,12 @@ void CairoOutputDev::endString(GfxState *state) goto finish; } + if (state->getFont()->getType() == fontType3 && render != 7) { + // If the current font is a type 3 font, we should ignore the text rendering mode + // (and use the default of 0) as long as we are going to either fill or stroke. + render = 0; + } + if (!(render & 1)) { LOG(printf("fill string\n")); cairo_set_source(cairo, fill_pattern); @@ -1921,21 +2354,23 @@ void CairoOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alpha /* convert to a luminocity map */ uint32_t *source_data = reinterpret_cast(cairo_image_surface_get_data(source)); - /* get stride in units of 32 bits */ - ptrdiff_t stride = cairo_image_surface_get_stride(source) / 4; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int lum = alpha ? fill_opacity : luminocity(source_data[y * stride + x]); - if (transferFunc) { - double lum_in, lum_out; - lum_in = lum / 256.0; - transferFunc->transform(&lum_in, &lum_out); - lum = (int)(lum_out * 255.0 + 0.5); + if (source_data) { + /* get stride in units of 32 bits */ + ptrdiff_t stride = cairo_image_surface_get_stride(source) / 4; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int lum = alpha ? fill_opacity : luminocity(source_data[y * stride + x]); + if (transferFunc) { + double lum_in, lum_out; + lum_in = lum / 256.0; + transferFunc->transform(&lum_in, &lum_out); + lum = (int)(lum_out * 255.0 + 0.5); + } + source_data[y * stride + x] = lum << 24; } - source_data[y * stride + x] = lum << 24; } + cairo_surface_mark_dirty(source); } - cairo_surface_mark_dirty(source); /* setup the new mask pattern */ mask = cairo_pattern_create_for_surface(source); @@ -2084,15 +2519,17 @@ void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, in /* work around a cairo bug when scaling 1x1 surfaces */ if (width == 1 && height == 1) { - ImageStream *imgStr; unsigned char pix; int invert_bit; - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - imgStr->getPixel(&pix); - imgStr->close(); - delete imgStr; + { + ImageStream imgStr(str, width, 1, 1); + if (!imgStr.reset()) { + return; + } + imgStr.getPixel(&pix); + imgStr.close(); + } invert_bit = invert ? 1 : 0; if (pix ^ invert_bit) { @@ -2118,17 +2555,7 @@ void CairoOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, in cairo_get_matrix(cairo, &matrix); // XXX: it is possible that we should only do sub pixel positioning if // we are rendering fonts */ - if (!printing - && prescaleImages - /* not rotated */ - && matrix.xy == 0 - && matrix.yx == 0 - /* axes not flipped / not 180 deg rotated */ - && matrix.xx > 0 && (upsideDown() ? -1 : 1) * matrix.yy > 0) { - drawImageMaskPrescaled(state, ref, str, width, height, invert, interpolate, inlineImg); - } else { - drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg); - } + drawImageMaskRegular(state, ref, str, width, height, invert, interpolate, inlineImg); } void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) @@ -2139,15 +2566,18 @@ void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stre /* work around a cairo bug when scaling 1x1 surfaces */ if (width == 1 && height == 1) { - ImageStream *imgStr; unsigned char pix; int invert_bit; - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - imgStr->getPixel(&pix); - imgStr->close(); - delete imgStr; + { + ImageStream imgStr(str, width, 1, 1); + if (imgStr.reset()) { + imgStr.getPixel(&pix); + } else { + pix = 0; + } + imgStr.close(); + } invert_bit = invert ? 1 : 0; if (!(pix ^ invert_bit)) { @@ -2171,11 +2601,7 @@ void CairoOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stre cairo_get_matrix(cairo, &matrix); // XXX: it is possible that we should only do sub pixel positioning if // we are rendering fonts */ - if (!printing && prescaleImages && matrix.xy == 0.0 && matrix.yx == 0.0) { - drawImageMaskPrescaled(state, ref, str, width, height, invert, false, inlineImg); - } else { - drawImageMaskRegular(state, ref, str, width, height, invert, false, inlineImg); - } + drawImageMaskRegular(state, ref, str, width, height, invert, false, inlineImg); if (state->getFillColorSpace()->getMode() == csPattern) { cairo_set_source_rgb(cairo, 1, 1, 1); @@ -2211,7 +2637,6 @@ void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream * cairo_surface_t *image; cairo_pattern_t *pattern; int x, y, i, bit; - ImageStream *imgStr; unsigned char *pix; cairo_matrix_t matrix; int invert_bit; @@ -2219,8 +2644,10 @@ void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream * cairo_filter_t filter; /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); + ImageStream imgStr(str, width, 1, 1); + if (!imgStr.reset()) { + return; + } image = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height); if (cairo_surface_status(image)) { @@ -2233,7 +2660,7 @@ void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream * invert_bit = invert ? 1 : 0; for (y = 0; y < height; y++) { - pix = imgStr->getLine(); + pix = imgStr.getLine(); dest = buffer + y * row_stride; i = 0; bit = 0; @@ -2310,304 +2737,11 @@ void CairoOutputDev::drawImageMaskRegular(GfxState *state, Object *ref, Stream * cairo_pattern_destroy(pattern); cleanup: - imgStr->close(); - delete imgStr; -} - -void CairoOutputDev::drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) -{ - unsigned char *buffer; - cairo_surface_t *image; - cairo_pattern_t *pattern; - ImageStream *imgStr; - unsigned char *pix; - cairo_matrix_t matrix; - int invert_bit; - ptrdiff_t row_stride; - - /* cairo does a very poor job of scaling down images so we scale them ourselves */ - - LOG(printf("drawImageMaskPrescaled %dx%d\n", width, height)); - - /* this scaling code is adopted from the splash image scaling code */ - cairo_get_matrix(cairo, &matrix); -#if 0 - printf("[%f %f], [%f %f], %f %f\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); -#endif - /* this whole computation should be factored out */ - double xScale = matrix.xx; - double yScale = matrix.yy; - int tx, tx2, ty, ty2; /* the integer co-ordinates of the resulting image */ - int scaledHeight; - int scaledWidth; - if (xScale >= 0) { - tx = splashRound(matrix.x0 - 0.01); - tx2 = splashRound(matrix.x0 + xScale + 0.01) - 1; - } else { - tx = splashRound(matrix.x0 + 0.01) - 1; - tx2 = splashRound(matrix.x0 + xScale - 0.01); - } - scaledWidth = abs(tx2 - tx) + 1; - // scaledWidth = splashRound(fabs(xScale)); - if (scaledWidth == 0) { - // technically, this should draw nothing, but it generally seems - // better to draw a one-pixel-wide stripe rather than throwing it - // away - scaledWidth = 1; - } - if (yScale >= 0) { - ty = splashFloor(matrix.y0 + 0.01); - ty2 = splashCeil(matrix.y0 + yScale - 0.01); - } else { - ty = splashCeil(matrix.y0 - 0.01); - ty2 = splashFloor(matrix.y0 + yScale + 0.01); - } - scaledHeight = abs(ty2 - ty); - if (scaledHeight == 0) { - scaledHeight = 1; - } -#if 0 - printf("xscale: %g, yscale: %g\n", xScale, yScale); - printf("width: %d, height: %d\n", width, height); - printf("scaledWidth: %d, scaledHeight: %d\n", scaledWidth, scaledHeight); -#endif - - /* compute the required padding */ - /* Padding is used to preserve the aspect ratio. - We compute total_pad to make (height+total_pad)/scaledHeight as close to height/yScale as possible */ - int head_pad = 0; - int tail_pad = 0; - int total_pad = splashRound(height * (scaledHeight / fabs(yScale)) - height); - - /* compute the two pieces of padding */ - if (total_pad > 0) { - // XXX: i'm not positive fabs() is correct - float tail_error = fabs(matrix.y0 - ty); - float head_error = fabs(ty2 - (matrix.y0 + yScale)); - float tail_fraction = tail_error / (tail_error + head_error); - tail_pad = splashRound(total_pad * tail_fraction); - head_pad = total_pad - tail_pad; - } else { - tail_pad = 0; - head_pad = 0; - } - int origHeight = height; - height += tail_pad; - height += head_pad; -#if 0 - printf("head_pad: %d tail_pad: %d\n", head_pad, tail_pad); - printf("origHeight: %d height: %d\n", origHeight, height); - printf("ty: %d, ty2: %d\n", ty, ty2); -#endif - - /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, 1, 1); - imgStr->reset(); - - invert_bit = invert ? 1 : 0; - - image = cairo_image_surface_create(CAIRO_FORMAT_A8, scaledWidth, scaledHeight); - if (cairo_surface_status(image)) { - imgStr->close(); - delete imgStr; - return; - } - - buffer = cairo_image_surface_get_data(image); - row_stride = cairo_image_surface_get_stride(image); - - int yp = height / scaledHeight; - int yq = height % scaledHeight; - int xp = width / scaledWidth; - int xq = width % scaledWidth; - int yt = 0; - int origHeight_c = origHeight; - /* use MIN() because yp might be > origHeight because of padding */ - unsigned char *pixBuf = (unsigned char *)malloc(MIN(yp + 1, origHeight) * width); - int lastYStep = 1; - int total = 0; - for (int y = 0; y < scaledHeight; y++) { - // y scale Bresenham - int yStep = yp; - yt += yq; - - if (yt >= scaledHeight) { - yt -= scaledHeight; - ++yStep; - } - - // read row (s) from image ignoring the padding as appropriate - { - int n = (yp > 0) ? yStep : lastYStep; - total += n; - if (n > 0) { - unsigned char *p = pixBuf; - int head_pad_count = head_pad; - int origHeight_count = origHeight; - int tail_pad_count = tail_pad; - for (int i = 0; i < n; i++) { - // get row - if (head_pad_count) { - head_pad_count--; - } else if (origHeight_count) { - pix = imgStr->getLine(); - for (int j = 0; j < width; j++) { - if (pix[j] ^ invert_bit) { - p[j] = 0; - } else { - p[j] = 255; - } - } - origHeight_count--; - p += width; - } else if (tail_pad_count) { - tail_pad_count--; - } else { - printf("%d %d\n", n, total); - assert(0 && "over run\n"); - } - } - } - } - - lastYStep = yStep; - - int xt = 0; - int xSrc = 0; - int n = yStep > 0 ? yStep : 1; - int origN = n; - - /* compute the size of padding and pixels that will be used for this row */ - int head_pad_size = MIN(n, head_pad); - n -= head_pad_size; - head_pad -= MIN(head_pad_size, yStep); - - int pix_size = MIN(n, origHeight); - n -= pix_size; - origHeight -= MIN(pix_size, yStep); - - int tail_pad_size = MIN(n, tail_pad); - n -= tail_pad_size; - tail_pad -= MIN(tail_pad_size, yStep); - if (n != 0) { - printf("n = %d (%d %d %d)\n", n, head_pad_size, pix_size, tail_pad_size); - assert(n == 0); - } - - for (int x = 0; x < scaledWidth; ++x) { - int xStep = xp; - xt += xq; - if (xt >= scaledWidth) { - xt -= scaledWidth; - ++xStep; - } - int m = xStep > 0 ? xStep : 1; - float pixAcc0 = 0; - /* could m * head_pad_size * tail_pad_size overflow? */ - if (invert_bit) { - pixAcc0 += m * head_pad_size * tail_pad_size * 255; - } else { - pixAcc0 += m * head_pad_size * tail_pad_size * 0; - } - /* Accumulate all of the source pixels for the destination pixel */ - for (int i = 0; i < pix_size; ++i) { - for (int j = 0; j < m; ++j) { - if (xSrc + i * width + j > MIN(yp + 1, origHeight_c) * width) { - printf("%d > %d (%d %d %d %d) (%d %d %d)\n", xSrc + i * width + j, MIN(yp + 1, origHeight_c) * width, xSrc, i, width, j, yp, origHeight_c, width); - printf("%d %d %d\n", head_pad_size, pix_size, tail_pad_size); - assert(0 && "bad access\n"); - } - pixAcc0 += pixBuf[xSrc + i * width + j]; - } - } - buffer[y * row_stride + x] = splashFloor(pixAcc0 / (origN * m)); - xSrc += xStep; - } - } - free(pixBuf); - - cairo_surface_mark_dirty(image); - pattern = cairo_pattern_create_for_surface(image); - cairo_surface_destroy(image); - if (cairo_pattern_status(pattern)) { - imgStr->close(); - delete imgStr; - return; - } - - /* we should actually be using CAIRO_FILTER_NEAREST here. However, - * cairo doesn't yet do minifaction filtering causing scaled down - * images with CAIRO_FILTER_NEAREST to look really bad */ - cairo_pattern_set_filter(pattern, interpolate ? CAIRO_FILTER_GOOD : CAIRO_FILTER_FAST); - - if (state->getFillColorSpace()->getMode() == csPattern) { - cairo_matrix_init_translate(&matrix, 0, scaledHeight); - cairo_matrix_scale(&matrix, scaledWidth, -scaledHeight); - cairo_pattern_set_matrix(pattern, &matrix); - if (cairo_pattern_status(pattern)) { - cairo_pattern_destroy(pattern); - imgStr->close(); - delete imgStr; - return; - } - - mask = cairo_pattern_reference(pattern); - cairo_get_matrix(cairo, &mask_matrix); - } else { - cairo_save(cairo); - - /* modify our current transformation so that the prescaled image - * goes where it is supposed to */ - cairo_get_matrix(cairo, &matrix); - cairo_scale(cairo, 1.0 / matrix.xx, 1.0 / matrix.yy); - // get integer co-ords - cairo_translate(cairo, tx - matrix.x0, ty2 - matrix.y0); - if (yScale > 0) { - cairo_scale(cairo, 1, -1); - } - - cairo_rectangle(cairo, 0., 0., scaledWidth, scaledHeight); - cairo_clip(cairo); - if (strokePathClip) { - cairo_push_group(cairo); - fillToStrokePathClip(state); - cairo_pop_group_to_source(cairo); - } - cairo_mask(cairo, pattern); - - // cairo_get_matrix(cairo, &matrix); - // printf("mask at: [%f %f], [%f %f], %f %f\n\n", matrix.xx, matrix.xy, matrix.yx, matrix.yy, matrix.x0, matrix.y0); - cairo_restore(cairo); - } - - if (cairo_shape) { - cairo_save(cairo_shape); - - /* modify our current transformation so that the prescaled image - * goes where it is supposed to */ - cairo_get_matrix(cairo_shape, &matrix); - cairo_scale(cairo_shape, 1.0 / matrix.xx, 1.0 / matrix.yy); - // get integer co-ords - cairo_translate(cairo_shape, tx - matrix.x0, ty2 - matrix.y0); - if (yScale > 0) { - cairo_scale(cairo_shape, 1, -1); - } - - cairo_rectangle(cairo_shape, 0., 0., scaledWidth, scaledHeight); - cairo_fill(cairo_shape); - - cairo_restore(cairo_shape); - } - - cairo_pattern_destroy(pattern); - - imgStr->close(); - delete imgStr; + imgStr.close(); } void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) { - ImageStream *maskImgStr, *imgStr; ptrdiff_t row_stride; unsigned char *maskBuffer, *buffer; unsigned char *maskDest; @@ -2622,13 +2756,14 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, cairo_filter_t filter; cairo_filter_t maskFilter; - maskImgStr = new ImageStream(maskStr, maskWidth, 1, 1); - maskImgStr->reset(); + ImageStream maskImgStr(maskStr, maskWidth, 1, 1); + if (!maskImgStr.reset()) { + return; + } maskImage = cairo_image_surface_create(CAIRO_FORMAT_A8, maskWidth, maskHeight); if (cairo_surface_status(maskImage)) { - maskImgStr->close(); - delete maskImgStr; + maskImgStr.close(); return; } @@ -2638,7 +2773,7 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, invert_bit = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; y++) { - pix = maskImgStr->getLine(); + pix = maskImgStr.getLine(); maskDest = maskBuffer + y * row_stride; for (x = 0; x < maskWidth; x++) { if (pix[x] ^ invert_bit) { @@ -2649,8 +2784,7 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, } } - maskImgStr->close(); - delete maskImgStr; + maskImgStr.close(); maskFilter = getFilterForSurface(maskImage, maskInterpolate); @@ -2666,13 +2800,15 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, * so check its underlying color space as well */ int is_identity_transform; is_identity_transform = colorMap->getColorSpace()->getMode() == csDeviceRGB || - (colorMap->getColorSpace()->getMode() == csICCBased && + (colorMap->getColorSpace()->getMode() == csICCBased && ((GfxICCBasedColorSpace*)colorMap->getColorSpace())->getAlt()->getMode() == csDeviceRGB); #endif /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgStr.reset()) { + return; + } image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(image)) { @@ -2683,7 +2819,7 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, row_stride = cairo_image_surface_get_stride(image); for (y = 0; y < height; y++) { dest = reinterpret_cast(buffer + y * row_stride); - pix = imgStr->getLine(); + pix = imgStr.getLine(); colorMap->getRGBLine(pix, dest, width); } @@ -2752,16 +2888,37 @@ void CairoOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, cairo_pattern_destroy(pattern); cleanup: - imgStr->close(); - delete imgStr; + imgStr.close(); +} + +static inline void getMatteColorRgb(GfxImageColorMap *colorMap, const GfxColor *matteColorIn, GfxRGB *matteColorRgb) +{ + colorMap->getColorSpace()->getRGB(matteColorIn, matteColorRgb); + matteColorRgb->r = colToByte(matteColorRgb->r); + matteColorRgb->g = colToByte(matteColorRgb->g); + matteColorRgb->b = colToByte(matteColorRgb->b); +} + +static inline void applyMask(unsigned int *imagePointer, int length, GfxRGB matteColor, unsigned char *alphaPointer) +{ + unsigned char *p, r, g, b; + int i; + + for (i = 0, p = (unsigned char *)imagePointer; i < length; i++, p += 4, alphaPointer++) { + if (*alphaPointer) { + b = std::clamp(matteColor.b + (int)(p[0] - matteColor.b) * 255 / *alphaPointer, 0, 255); + g = std::clamp(matteColor.g + (int)(p[1] - matteColor.g) * 255 / *alphaPointer, 0, 255); + r = std::clamp(matteColor.r + (int)(p[2] - matteColor.r) * 255 / *alphaPointer, 0, 255); + imagePointer[i] = (r << 16) | (g << 8) | (b << 0); + } + } } // XXX: is this affect by AIS(alpha is shape)? void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, bool maskInterpolate) { - ImageStream *maskImgStr, *imgStr; - ptrdiff_t row_stride; + ptrdiff_t row_stride, mask_row_stride; unsigned char *maskBuffer, *buffer; unsigned char *maskDest; unsigned int *dest; @@ -2772,29 +2929,46 @@ void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *s int y; cairo_filter_t filter; cairo_filter_t maskFilter; + GfxRGB matteColorRgb; + + // Clamp heights to what Cairo can handle - Issue #991 + if (height >= MAX_CAIRO_IMAGE_SIZE) { + error(errInternal, -1, "Reducing image height from {0:d} to {1:d} because of Cairo limits", height, MAX_CAIRO_IMAGE_SIZE - 1); + height = MAX_CAIRO_IMAGE_SIZE - 1; + } + + if (maskHeight >= MAX_CAIRO_IMAGE_SIZE) { + error(errInternal, -1, "Reducing maskImage height from {0:d} to {1:d} because of Cairo limits", maskHeight, MAX_CAIRO_IMAGE_SIZE - 1); + maskHeight = MAX_CAIRO_IMAGE_SIZE - 1; + } + + const GfxColor *matteColor = maskColorMap->getMatteColor(); + if (matteColor != nullptr) { + getMatteColorRgb(colorMap, matteColor, &matteColorRgb); + } - maskImgStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); - maskImgStr->reset(); + ImageStream maskImgStr(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); + if (!maskImgStr.reset()) { + return; + } maskImage = cairo_image_surface_create(CAIRO_FORMAT_A8, maskWidth, maskHeight); if (cairo_surface_status(maskImage)) { - maskImgStr->close(); - delete maskImgStr; + maskImgStr.close(); return; } maskBuffer = cairo_image_surface_get_data(maskImage); - row_stride = cairo_image_surface_get_stride(maskImage); + mask_row_stride = cairo_image_surface_get_stride(maskImage); for (y = 0; y < maskHeight; y++) { - maskDest = (unsigned char *)(maskBuffer + y * row_stride); - pix = maskImgStr->getLine(); + maskDest = (unsigned char *)(maskBuffer + y * mask_row_stride); + pix = maskImgStr.getLine(); if (likely(pix != nullptr)) { maskColorMap->getGrayLine(pix, maskDest, maskWidth); } } - maskImgStr->close(); - delete maskImgStr; + maskImgStr.close(); maskFilter = getFilterForSurface(maskImage, maskInterpolate); @@ -2815,8 +2989,10 @@ void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *s #endif /* TODO: Do we want to cache these? */ - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgStr.reset()) { + return; + } image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); if (cairo_surface_status(image)) { @@ -2827,15 +3003,23 @@ void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *s row_stride = cairo_image_surface_get_stride(image); for (y = 0; y < height; y++) { dest = reinterpret_cast(buffer + y * row_stride); - pix = imgStr->getLine(); - colorMap->getRGBLine(pix, dest, width); + pix = imgStr.getLine(); + if (likely(pix != nullptr)) { + colorMap->getRGBLine(pix, dest, width); + if (matteColor != nullptr) { + maskDest = (unsigned char *)(maskBuffer + y * mask_row_stride); + applyMask(dest, width, matteColorRgb, maskDest); + } + } } filter = getFilterForSurface(image, interpolate); cairo_surface_mark_dirty(image); - setMimeData(state, str, ref, colorMap, image, height); + if (matteColor == nullptr) { + setMimeData(state, str, ref, colorMap, image, height); + } pattern = cairo_pattern_create_for_surface(image); cairo_surface_destroy(image); @@ -2911,8 +3095,7 @@ void CairoOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *s cairo_pattern_destroy(pattern); cleanup: - imgStr->close(); - delete imgStr; + imgStr.close(); } bool CairoOutputDev::getStreamData(Stream *str, char **buffer, int *length) @@ -2922,18 +3105,22 @@ bool CairoOutputDev::getStreamData(Stream *str, char **buffer, int *length) len = 0; str->close(); - str->reset(); + if (!str->reset()) { + return false; + } while (str->getChar() != EOF) { len++; } if (len == 0) { return false; } + str->close(); + if (!str->reset()) { + return false; + } strBuffer = (char *)gmalloc(len); - str->close(); - str->reset(); for (i = 0; i < len; ++i) { strBuffer[i] = str->getChar(); } @@ -2954,32 +3141,26 @@ static bool colorMapHasIdentityDecodeMap(GfxImageColorMap *colorMap) return true; } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) static cairo_status_t setMimeIdFromRef(cairo_surface_t *surface, const char *mime_type, const char *mime_id_prefix, Ref ref) { - GooString *mime_id; + GooString mime_id; char *idBuffer; cairo_status_t status; - mime_id = new GooString; - if (mime_id_prefix) { - mime_id->append(mime_id_prefix); + mime_id.append(mime_id_prefix); } - mime_id->appendf("{0:d}-{1:d}", ref.gen, ref.num); + mime_id.appendf("{0:d}-{1:d}", ref.gen, ref.num); - idBuffer = copyString(mime_id->c_str()); - status = cairo_surface_set_mime_data(surface, mime_type, (const unsigned char *)idBuffer, mime_id->getLength(), gfree, idBuffer); - delete mime_id; + idBuffer = copyString(mime_id.c_str()); + status = cairo_surface_set_mime_data(surface, mime_type, (const unsigned char *)idBuffer, mime_id.getLength(), gfree, idBuffer); if (status) { gfree(idBuffer); } return status; } -#endif -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) bool CairoOutputDev::setMimeDataForJBIG2Globals(Stream *str, cairo_surface_t *image) { JBIG2Stream *jb2Str = static_cast(str); @@ -3007,9 +3188,7 @@ bool CairoOutputDev::setMimeDataForJBIG2Globals(Stream *str, cairo_surface_t *im return true; } -#endif -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10) bool CairoOutputDev::setMimeDataForCCITTParams(Stream *str, cairo_surface_t *image, int height) { CCITTFaxStream *ccittStr = static_cast(str); @@ -3032,14 +3211,12 @@ bool CairoOutputDev::setMimeDataForCCITTParams(Stream *str, cairo_surface_t *ima return true; } -#endif void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxImageColorMap *colorMap, cairo_surface_t *image, int height) { char *strBuffer; int len; Object obj; - GfxColorSpace *colorSpace; StreamKind strKind = str->getKind(); const char *mime_type; cairo_status_t status; @@ -3048,8 +3225,7 @@ void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxI return; } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) - // Since 1.5.10 the cairo PS backend stores images with UNIQUE_ID in PS memory so the + // The cairo PS backend stores images with UNIQUE_ID in PS memory so the // image can be re-used multiple times. As we don't know how large the images are or // how many times they are used, there is no benefit in enabling this. Issue #106 if (cairo_surface_get_type(cairo_get_target(cairo)) != CAIRO_SURFACE_TYPE_PS) { @@ -3060,7 +3236,6 @@ void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxI } } } -#endif switch (strKind) { case strDCT: @@ -3069,23 +3244,19 @@ void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxI case strJPX: mime_type = CAIRO_MIME_TYPE_JP2; break; -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) case strJBIG2: mime_type = CAIRO_MIME_TYPE_JBIG2; break; -#endif -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10) case strCCITTFax: mime_type = CAIRO_MIME_TYPE_CCITT_FAX; break; -#endif default: mime_type = nullptr; break; } obj = str->getDict()->lookup("ColorSpace"); - colorSpace = GfxColorSpace::parse(nullptr, &obj, this, state); + std::unique_ptr colorSpace = GfxColorSpace::parse(nullptr, &obj, this, state); // colorspace in stream dict may be different from colorspace in jpx // data @@ -3096,11 +3267,11 @@ void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxI // only embed mime data for gray, rgb, and cmyk colorspaces. if (colorSpace) { GfxColorSpaceMode mode = colorSpace->getMode(); - delete colorSpace; switch (mode) { case csDeviceGray: case csCalGray: case csDeviceRGB: + case csDeviceRGBA: case csCalRGB: case csDeviceCMYK: case csICCBased: @@ -3119,17 +3290,13 @@ void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref, GfxI return; } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) if (strKind == strJBIG2 && !setMimeDataForJBIG2Globals(str, image)) { return; } -#endif -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10) if (strKind == strCCITTFax && !setMimeDataForCCITTParams(str, image, height)) { return; } -#endif if (mime_type) { if (getStreamData(str->getNextStream(), &strBuffer, &len)) { @@ -3152,6 +3319,7 @@ class RescaleDrawImage : public CairoRescaleBox const int *maskColors; int current_row; bool imageError; + bool fromRGBA; public: ~RescaleDrawImage() override; @@ -3166,10 +3334,14 @@ class RescaleDrawImage : public CairoRescaleBox width = widthA; current_row = -1; imageError = false; + fromRGBA = colorMap->getColorSpace()->getMode() == csDeviceRGBA; /* TODO: Do we want to cache these? */ imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + delete imgStr; + return image; + } #if 0 /* ICCBased color space doesn't do any color correction @@ -3195,11 +3367,7 @@ class RescaleDrawImage : public CairoRescaleBox } } -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) - bool needsCustomDownscaling = false; -#else - bool needsCustomDownscaling = true; -#endif + bool needsCustomDownscaling = (width > MAX_CAIRO_IMAGE_SIZE || height > MAX_CAIRO_IMAGE_SIZE); if (printing) { if (width > MAX_PRINT_IMAGE_SIZE || height > MAX_PRINT_IMAGE_SIZE) { @@ -3218,8 +3386,6 @@ class RescaleDrawImage : public CairoRescaleBox if (scaledHeight == 0) { scaledHeight = 1; } - } else { - needsCustomDownscaling = false; } } @@ -3228,7 +3394,8 @@ class RescaleDrawImage : public CairoRescaleBox unsigned char *buffer; ptrdiff_t stride; - image = cairo_image_surface_create(maskColors ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height); + image = cairo_image_surface_create(maskColors || fromRGBA ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height); + if (cairo_surface_status(image)) { goto cleanup; } @@ -3240,14 +3407,14 @@ class RescaleDrawImage : public CairoRescaleBox getRow(y, dest); } } else { - // // Downscaling required. Create cairo image the size of the - // rescaled image and // downscale the source image data into + // Downscaling required. Create cairo image the size of the + // rescaled image and downscale the source image data into // the cairo image. downScaleImage() will call getRow() to read // source image data from the image stream. This avoids having // to create an image the size of the source image which may - // exceed cairo's 32676x32767 image size limit (and also saves a + // exceed cairo's 32767x32767 image size limit (and also saves a // lot of memory). - image = cairo_image_surface_create(maskColors ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, scaledWidth, scaledHeight); + image = cairo_image_surface_create(maskColors || fromRGBA ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, scaledWidth, scaledHeight); if (cairo_surface_status(image)) { goto cleanup; } @@ -3291,6 +3458,15 @@ class RescaleDrawImage : public CairoRescaleBox row_data[i] = ((int)colToByte(rgb.r) << 16) | ((int)colToByte(rgb.g) << 8) | ((int)colToByte(rgb.b) << 0); p++; } + } else if (fromRGBA) { + // Case of transparent JPX images, they contain RGBA data · Issue #1486 + GfxDeviceRGBAColorSpace *rgbaCS = dynamic_cast(colorMap->getColorSpace()); + if (rgbaCS) { + rgbaCS->getARGBPremultipliedLine(pix, row_data, width); + } else { + error(errSyntaxWarning, -1, "CairoOutputDev: Unexpected fallback from RGBA to RGB"); + colorMap->getRGBLine(pix, row_data, width); + } } else { colorMap->getRGBLine(pix, row_data, width); } @@ -3344,16 +3520,7 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int wi } if (!inlineImg) { /* don't read stream twice if it is an inline image */ - // cairo 1.15.10 allows mime image data to have different size to cairo image - // mime image size will be scaled to same size as cairo image -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10) - bool requireSameSize = false; -#else - bool requireSameSize = true; -#endif - if (!requireSameSize || (width == widthA && height == heightA)) { - setMimeData(state, str, ref, colorMap, image, heightA); - } + setMimeData(state, str, ref, colorMap, image, heightA); } pattern = cairo_pattern_create_for_surface(image); @@ -3423,6 +3590,58 @@ void CairoOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int wi cairo_pattern_destroy(pattern); } +void CairoOutputDev::beginMarkedContent(const char *name, Dict *properties) +{ + if (!logicalStruct || !isPDF()) { + return; + } + + if (strcmp(name, "Artifact") == 0) { + markedContentStack.emplace_back(name); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_tag_begin(cairo, name, nullptr); +#endif + return; + } + + int mcid = -1; + if (properties) { + properties->lookupInt("MCID", nullptr, &mcid); + } + + if (mcid == -1) { + return; + } + + GooString attribs; + attribs.appendf("tag_name='{0:s}' id='{1:d}_{2:d}'", name, currentStructParents, mcid); + mcidEmitted.insert(std::pair(currentStructParents, mcid)); + + std::string tag; +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + tag = CAIRO_TAG_CONTENT; + cairo_tag_begin(cairo, CAIRO_TAG_CONTENT, attribs.c_str()); +#endif + + markedContentStack.push_back(tag); +} + +void CairoOutputDev::endMarkedContent(GfxState *state) +{ + if (!logicalStruct || !isPDF()) { + return; + } + + if (markedContentStack.empty()) { + return; + } + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + cairo_tag_end(cairo, markedContentStack.back().c_str()); +#endif + markedContentStack.pop_back(); +} + //------------------------------------------------------------------------ // ImageOutputDev //------------------------------------------------------------------------ @@ -3443,14 +3662,14 @@ CairoImageOutputDev::~CairoImageOutputDev() for (i = 0; i < numImages; i++) { delete images[i]; } - gfree(images); + gfree(static_cast(images)); } void CairoImageOutputDev::saveImage(CairoImage *image) { if (numImages >= size) { size += 16; - images = (CairoImage **)greallocn(images, size, sizeof(CairoImage *)); + images = (CairoImage **)greallocn(static_cast(images), size, sizeof(CairoImage *)); } images[numImages++] = image; } diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h index 5ce877d..4051f0a 100644 --- a/poppler/CairoOutputDev.h +++ b/poppler/CairoOutputDev.h @@ -18,7 +18,7 @@ // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2005 Nickolay V. Shmyrev // Copyright (C) 2006-2011, 2013 Carlos Garcia Campos -// Copyright (C) 2008, 2009, 2011-2017, 2022 Adrian Johnson +// Copyright (C) 2008, 2009, 2011-2017, 2022-2024 Adrian Johnson // Copyright (C) 2008 Michael Vrable // Copyright (C) 2010-2013 Thomas Freitag // Copyright (C) 2015 Suzuki Toshiya @@ -28,6 +28,7 @@ // Copyright (C) 2020 Michal // Copyright (C) 2021 Christian Persch // Copyright (C) 2022 Marek Kasik +// Copyright (C) 2024 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -37,10 +38,16 @@ #ifndef CAIROOUTPUTDEV_H #define CAIROOUTPUTDEV_H +#include + #include #include "OutputDev.h" #include "TextOutputDev.h" #include "GfxState.h" +#include "StructElement.h" +#include "StructTreeRoot.h" +#include "Annot.h" +#include "Link.h" class PDFDoc; class GfxState; @@ -118,37 +125,18 @@ class CairoOutputDev : public OutputDev // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) - bool useShadedFills(int type) override - { - return type <= 7; - } -#else - bool useShadedFills(int type) override - { - return type > 1 && type < 4; - } -#endif + bool useShadedFills(int type) override { return type <= 7; } // Does this device use FillColorStop()? - bool useFillColorStop() override - { - return true; - } + bool useFillColorStop() override { return true; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. - bool interpretType3Chars() override - { - return false; - } + bool interpretType3Chars() override { return false; } // Does this device need to clip pages to the crop box even when the // box is the crop box? - bool needClipToCropBox() override - { - return true; - } + bool needClipToCropBox() override { return true; } //----- initialization and control @@ -158,6 +146,12 @@ class CairoOutputDev : public OutputDev // End a page. void endPage() override; + // Must be called before last call to endPage() + void emitStructTree(); + + void beginForm(Object *obj, Ref id) override; + void endForm(Object *obj, Ref id) override; + //----- save/restore graphics state void saveState(GfxState *state) override; void restoreState(GfxState *state) override; @@ -188,17 +182,13 @@ class CairoOutputDev : public OutputDev void eoFill(GfxState *state) override; void clipToStrokePath(GfxState *state) override; bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override; -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) bool functionShadedFill(GfxState *state, GfxFunctionShading *shading) override; -#endif bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override; bool axialShadedSupportExtend(GfxState *state, GfxAxialShading *shading) override; bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) override; bool radialShadedSupportExtend(GfxState *state, GfxRadialShading *shading) override; -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) override; bool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) override; -#endif //----- path clipping void clip(GfxState *state) override; @@ -216,11 +206,13 @@ class CairoOutputDev : public OutputDev void beginTextObject(GfxState *state) override; void endTextObject(GfxState *state) override; + void beginMarkedContent(const char *name, Dict *properties) override; + void endMarkedContent(GfxState *state) override; + //----- image drawing void drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) override; void setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool inlineImg, double *baseMatrix) override; void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) override; - void drawImageMaskPrescaled(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg); void drawImageMaskRegular(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg); void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; @@ -229,6 +221,9 @@ class CairoOutputDev : public OutputDev void drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) override; + // Does this device supports transparency (alpha channel) in JPX streams? + bool supportJPXtransparency() override { return true; } + //----- transparency groups and soft masks void beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) override; void endTransparencyGroup(GfxState * /*state*/) override; @@ -249,10 +244,7 @@ class CairoOutputDev : public OutputDev // Called to prepare this output dev for rendering CairoType3Font. void startType3Render(GfxState *state, XRef *xref); - bool isReverseVideo() - { - return false; - } + bool isReverseVideo() { return false; } void setCairo(cairo_t *cr); void setTextPage(TextPage *text); @@ -262,28 +254,23 @@ class CairoOutputDev : public OutputDev needFontUpdate = true; } void copyAntialias(cairo_t *cr, cairo_t *source_cr); + void setLogicalStructure(bool logStruct) { this->logicalStruct = logStruct; } - void setInType3Char(bool inType3CharA) + enum Type3RenderType { - inType3Char = inType3CharA; - } + Type3RenderNone, + Type3RenderMask, + Type3RenderColor + }; + void setType3RenderType(Type3RenderType state) { t3_render_state = state; } void getType3GlyphWidth(double *wx, double *wy) { *wx = t3_glyph_wx; *wy = t3_glyph_wy; } - bool hasType3GlyphBBox() - { - return t3_glyph_has_bbox; - } - double *getType3GlyphBBox() - { - return t3_glyph_bbox; - } - bool type3GlyphHasColor() - { - return t3_glyph_has_color; - } + bool hasType3GlyphBBox() { return t3_glyph_has_bbox; } + double *getType3GlyphBBox() { return t3_glyph_bbox; } + bool type3GlyphHasColor() { return t3_glyph_has_color; } protected: void doPath(cairo_t *cairo, GfxState *state, const GfxPath *path); @@ -294,12 +281,21 @@ class CairoOutputDev : public OutputDev void setMimeData(GfxState *state, Stream *str, Object *ref, GfxImageColorMap *colorMap, cairo_surface_t *image, int height); void fillToStrokePathClip(GfxState *state); void alignStrokeCoords(const GfxSubpath *subpath, int i, double *x, double *y); -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0) + AnnotLink *findLinkObject(const StructElement *elem); + void quadToCairoRect(AnnotQuadrilaterals *quads, int idx, double destPageHeight, cairo_rectangle_t *rect); + bool appendLinkDestRef(GooString *s, const LinkDest *dest); + void appendLinkDestXY(GooString *s, const LinkDest *dest, double destPageHeight); + bool beginLinkTag(AnnotLink *annotLink); + bool beginLink(const StructElement *linkElem); + void getStructElemAttributeString(const StructElement *elem); + int getContentElementStructParents(const StructElement *element); + bool checkIfStructElementNeeded(const StructElement *element); + void emitStructElement(const StructElement *elem); + void startFirstPage(int pageNum, GfxState *state, XRef *xrefA); bool setMimeDataForJBIG2Globals(Stream *str, cairo_surface_t *image); -#endif -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10) bool setMimeDataForCCITTParams(Stream *str, cairo_surface_t *image, int height); -#endif + static void textStringToQuotedUtf8(const GooString *text, GooString *s); + bool isPDF(); std::optional fill_color, stroke_color; cairo_pattern_t *fill_pattern, *stroke_pattern; @@ -323,7 +319,7 @@ class CairoOutputDev : public OutputDev cairo_line_join_t join; double miter; int ref_count; - } * strokePathClip; + } *strokePathClip; PDFDoc *doc; // the current document @@ -348,13 +344,20 @@ class CairoOutputDev : public OutputDev int utf8Max; cairo_path_t *textClipPath; bool inUncoloredPattern; // inside a uncolored pattern (PaintType = 2) - bool inType3Char; // inside a Type 3 CharProc + Type3RenderType t3_render_state; double t3_glyph_wx, t3_glyph_wy; bool t3_glyph_has_bbox; bool t3_glyph_has_color; bool has_color; double t3_glyph_bbox[4]; - bool prescaleImages; + bool logicalStruct; + bool firstPage; + int pdfPageNum; // page number of the PDF file + int cairoPageNum; // page number in cairo output + std::vector markedContentStack; + std::vector annotations; + std::set emittedDestinations; + std::map pdfPageToCairoPageMap; TextPage *textPage; // text for the current page ActualText *actualText; @@ -371,7 +374,7 @@ class CairoOutputDev : public OutputDev GfxColorSpace *cs; cairo_matrix_t group_matrix; struct ColorSpaceStack *next; - } * groupColorSpaceStack; + } *groupColorSpaceStack; struct SaveStateElement { @@ -385,6 +388,19 @@ class CairoOutputDev : public OutputDev Ref fontRef; }; std::vector saveStateStack; + + std::map>> destsMap; + std::map pdfPageRefToCairoPageNumMap; + std::vector structParentsStack; + int currentStructParents; + + struct StructParentsMcidHash + { + size_t operator()(std::pair x) const { return x.first << 16 | x.second; } + }; + std::unordered_set, StructParentsMcidHash> mcidEmitted; // + + std::unordered_set structElementNeeded; }; //------------------------------------------------------------------------ @@ -418,36 +434,17 @@ class CairoImageOutputDev : public CairoOutputDev // Does this device use functionShadedFill(), axialShadedFill(), and // radialShadedFill()? If this returns false, these shaded fills // will be reduced to a series of other drawing operations. -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 2) - bool useShadedFills(int type) override - { - return type <= 7; - } -#else - bool useShadedFills(int type) override - { - return type < 4; - } -#endif + bool useShadedFills(int type) override { return type <= 7; } // Does this device use FillColorStop()? - bool useFillColorStop() override - { - return false; - } + bool useFillColorStop() override { return false; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. - bool interpretType3Chars() override - { - return false; - } + bool interpretType3Chars() override { return false; } // Does this device need non-text content? - bool needNonText() override - { - return true; - } + bool needNonText() override { return true; } //----- save/restore graphics state void saveState(GfxState *state) override { } @@ -477,18 +474,9 @@ class CairoImageOutputDev : public CairoOutputDev void fill(GfxState *state) override { } void eoFill(GfxState *state) override { } void clipToStrokePath(GfxState *state) override { } - bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override - { - return true; - } - bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override - { - return true; - } - bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) override - { - return true; - } + bool tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) override { return true; } + bool axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) override { return true; } + bool radialShadedFill(GfxState *state, GfxRadialShading *shading, double sMin, double sMax) override { return true; } //----- path clipping void clip(GfxState *state) override { } @@ -518,14 +506,8 @@ class CairoImageOutputDev : public CairoOutputDev imgDrawCbkData = data; } // Iterate through list of images. - int getNumImages() const - { - return numImages; - } - CairoImage *getImage(int i) const - { - return images[i]; - } + int getNumImages() const { return numImages; } + CairoImage *getImage(int i) const { return images[i]; } private: void saveImage(CairoImage *image); diff --git a/poppler/CairoRescaleBox.cc b/poppler/CairoRescaleBox.cc index 4d09e58..a89f98a 100644 --- a/poppler/CairoRescaleBox.cc +++ b/poppler/CairoRescaleBox.cc @@ -45,7 +45,6 @@ #include -#include #include #include #include diff --git a/poppler/CairoRescaleBox.h b/poppler/CairoRescaleBox.h index 2766d48..99b6afd 100644 --- a/poppler/CairoRescaleBox.h +++ b/poppler/CairoRescaleBox.h @@ -45,8 +45,8 @@ class CairoRescaleBox { public: - CairoRescaleBox() {}; - virtual ~CairoRescaleBox() {}; + CairoRescaleBox() = default; + virtual ~CairoRescaleBox() = default; CairoRescaleBox(const CairoRescaleBox &) = delete; CairoRescaleBox &operator=(const CairoRescaleBox &) = delete; diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc index 7a1e409..c083923 100644 --- a/poppler/Catalog.cc +++ b/poppler/Catalog.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Kristian Høgsberg -// Copyright (C) 2005-2013, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005-2013, 2015, 2017-2024 Albert Astals Cid // Copyright (C) 2005 Jeff Muizelaar // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2005 Marco Pesenti Gritti @@ -40,6 +40,10 @@ // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright (C) 2021 RM +// Copyright (C) 2023 Ilaï Deutel +// Copyright (C) 2024 Hubert Figuiere +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2025 Aaron Nguyen // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -50,7 +54,6 @@ #include #include -#include "goo/gmem.h" #include "Object.h" #include "PDFDoc.h" #include "XRef.h" @@ -92,7 +95,6 @@ Catalog::Catalog(PDFDoc *docA) pagesList = nullptr; pagesRefList = nullptr; - attrsList = nullptr; kidsIdxList = nullptr; markInfo = markInfoNull; @@ -117,10 +119,9 @@ Catalog::Catalog(PDFDoc *docA) // get the Optional Content dictionary Object optContentProps = catDict.dictLookup("OCProperties"); if (optContentProps.isDict()) { - optContent = new OCGs(&optContentProps, xref); + optContent = std::make_unique(optContentProps, xref); if (!optContent->isOk()) { - delete optContent; - optContent = nullptr; + optContent.reset(); } } @@ -143,13 +144,6 @@ Catalog::Catalog(PDFDoc *docA) Catalog::~Catalog() { delete kidsIdxList; - if (attrsList) { - std::vector::iterator it; - for (it = attrsList->begin(); it != attrsList->end(); ++it) { - delete *it; - } - delete attrsList; - } delete pagesRefList; delete pagesList; delete destNameTree; @@ -157,7 +151,6 @@ Catalog::~Catalog() delete jsNameTree; delete pageLabelInfo; delete form; - delete optContent; delete viewerPrefs; delete structTreeRoot; } @@ -220,44 +213,80 @@ Ref *Catalog::getPageRef(int i) return &pages[i - 1].second; } -bool Catalog::cachePageTree(int page) +// Init page list. Return true on success, including if already inited. +bool Catalog::initPageList() { - if (pagesList == nullptr) { + // It's already initialized + if (pagesList != nullptr) { + return true; + } - Ref pagesRef; + Ref pagesRef; - Object catDict = xref->getCatalog(); + Object catDict = xref->getCatalog(); - if (catDict.isDict()) { - const Object &pagesDictRef = catDict.dictLookupNF("Pages"); - if (pagesDictRef.isRef() && pagesDictRef.getRefNum() >= 0 && pagesDictRef.getRefNum() < xref->getNumObjects()) { - pagesRef = pagesDictRef.getRef(); - } else { - error(errSyntaxError, -1, "Catalog dictionary does not contain a valid \"Pages\" entry"); - return false; - } + if (catDict.isDict()) { + const Object &pagesDictRef = catDict.dictLookupNF("Pages"); + if (pagesDictRef.isRef() && pagesDictRef.getRefNum() >= 0 && pagesDictRef.getRefNum() < xref->getNumObjects()) { + pagesRef = pagesDictRef.getRef(); } else { - error(errSyntaxError, -1, "Could not find catalog dictionary"); + error(errSyntaxError, -1, "Catalog dictionary does not contain a valid \"Pages\" entry"); return false; } + } else { + error(errSyntaxError, -1, "Could not find catalog dictionary"); + return false; + } - Object obj = catDict.dictLookup("Pages"); - // This should really be isDict("Pages"), but I've seen at least one - // PDF file where the /Type entry is missing. - if (!obj.isDict()) { - error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", obj.getTypeName()); - return false; + Object obj = catDict.dictLookup("Pages"); + // This should really be isDict("Pages"), but I've seen at least one + // PDF file where the /Type entry is missing. + if (!obj.isDict()) { + error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})", obj.getTypeName()); + return false; + } + + // If the current Pages object lacks a Kids array but has a Parent dictionary, traverse up the hierarchy + // until the root Pages object (one without a Parent) is found + if (!obj.dictLookup("Kids").isArray() && obj.dictLookup("Parent").isDict()) { + RefRecursionChecker seen; + while (obj.isDict() && obj.dictLookup("Parent").isDict()) { + Object parentDictObj = obj.dictLookup("Parent"); + + if (!seen.insert(pagesRef)) { + error(errSyntaxError, -1, "Loop detected in Pages tree (numObj: {0:d})", pagesRef.num); + break; + } + + if (parentDictObj.isDict()) { + const Object &parentRefObj = obj.dictLookupNF("Parent"); + if (parentRefObj.isRef()) { + pagesRef = parentRefObj.getRef(); + } + obj = std::move(parentDictObj); + } else { + break; + } } + } - pages.clear(); - attrsList = new std::vector(); - attrsList->push_back(new PageAttrs(nullptr, obj.getDict())); - pagesList = new std::vector(); - pagesList->push_back(std::move(obj)); - pagesRefList = new std::vector(); - pagesRefList->push_back(pagesRef); - kidsIdxList = new std::vector(); - kidsIdxList->push_back(0); + pages.clear(); + refPageMap.clear(); + attrsList.push_back(std::make_unique(nullptr, obj.getDict())); + pagesList = new std::vector(); + pagesList->push_back(std::move(obj)); + pagesRefList = new std::vector(); + pagesRefList->push_back(pagesRef); + kidsIdxList = new std::vector(); + kidsIdxList->push_back(0); + + return true; +} + +bool Catalog::cachePageTree(int page) +{ + if (!initPageList()) { + return false; } while (true) { @@ -266,94 +295,121 @@ bool Catalog::cachePageTree(int page) return true; } - if (pagesList->empty()) { + if (!cacheSubTree()) { return false; } + } - Object kids = pagesList->back().dictLookup("Kids"); - if (!kids.isArray()) { - error(errSyntaxError, -1, "Kids object (page {0:uld}) is wrong type ({1:s})", pages.size() + 1, kids.getTypeName()); - return false; - } + return false; +} - int kidsIdx = kidsIdxList->back(); - if (kidsIdx >= kids.arrayGetLength()) { - pagesList->pop_back(); - pagesRefList->pop_back(); - delete attrsList->back(); - attrsList->pop_back(); - kidsIdxList->pop_back(); - if (!kidsIdxList->empty()) { - kidsIdxList->back()++; - } - continue; - } +// Cache the page tree until the page ref and return the page number if found. +// Or 0. +std::size_t Catalog::cachePageTreeForRef(const Ref untilPageRef) +{ + if (!initPageList()) { + return 0; + } + while (true) { - const Object &kidRef = kids.arrayGetNF(kidsIdx); - if (!kidRef.isRef()) { - error(errSyntaxError, -1, "Kid object (page {0:uld}) is not an indirect reference ({1:s})", pages.size() + 1, kidRef.getTypeName()); - return false; + auto iter = refPageMap.find(untilPageRef); + if (iter != refPageMap.end()) { + return iter->second; } - bool loop = false; - ; - for (const Ref &pageRef : *pagesRefList) { - if (pageRef.num == kidRef.getRefNum()) { - loop = true; - break; - } - } - if (loop) { - error(errSyntaxError, -1, "Loop in Pages tree"); - kidsIdxList->back()++; - continue; + if (!cacheSubTree()) { + return 0; } + } - Object kid = kids.arrayGet(kidsIdx); - if (kid.isDict("Page") || (kid.isDict() && !kid.getDict()->hasKey("Kids"))) { - PageAttrs *attrs = new PageAttrs(attrsList->back(), kid.getDict()); - auto p = std::make_unique(doc, pages.size() + 1, std::move(kid), kidRef.getRef(), attrs, form); - if (!p->isOk()) { - error(errSyntaxError, -1, "Failed to create page (page {0:uld})", pages.size() + 1); - return false; - } + return 0; +} - if (pages.size() >= std::size_t(numPages)) { - error(errSyntaxError, -1, "Page count in top-level pages object is incorrect"); - return false; - } +// Return true to continue, false on error. +bool Catalog::cacheSubTree() +{ + if (pagesList->empty()) { + return false; + } - pages.emplace_back(std::move(p), kidRef.getRef()); + Object kids = pagesList->back().dictLookup("Kids"); + if (!kids.isArray()) { + error(errSyntaxError, -1, "Kids object (page {0:uld}) is wrong type ({1:s})", pages.size() + 1, kids.getTypeName()); + return false; + } + int kidsIdx = kidsIdxList->back(); + if (kidsIdx >= kids.arrayGetLength()) { + pagesList->pop_back(); + pagesRefList->pop_back(); + attrsList.pop_back(); + kidsIdxList->pop_back(); + if (!kidsIdxList->empty()) { kidsIdxList->back()++; + } + return true; + } - // This should really be isDict("Pages"), but I've seen at least one - // PDF file where the /Type entry is missing. - } else if (kid.isDict()) { - attrsList->push_back(new PageAttrs(attrsList->back(), kid.getDict())); - pagesRefList->push_back(kidRef.getRef()); - pagesList->push_back(std::move(kid)); - kidsIdxList->push_back(0); - } else { - error(errSyntaxError, -1, "Kid object (page {0:uld}) is wrong type ({1:s})", pages.size() + 1, kid.getTypeName()); - kidsIdxList->back()++; + const Object &kidRef = kids.arrayGetNF(kidsIdx); + if (!kidRef.isRef()) { + error(errSyntaxError, -1, "Kid object (page {0:uld}) is not an indirect reference ({1:s})", pages.size() + 1, kidRef.getTypeName()); + return false; + } + + bool loop = false; + ; + for (const Ref &pageRef : *pagesRefList) { + if (pageRef.num == kidRef.getRefNum()) { + loop = true; + break; } } + if (loop) { + error(errSyntaxError, -1, "Loop in Pages tree"); + kidsIdxList->back()++; + return true; + } - return false; + Object kid = kids.arrayGet(kidsIdx); + if (kid.isDict("Page") || (kid.isDict() && !kid.getDict()->hasKey("Kids"))) { + auto attrs = std::make_unique(attrsList.back().get(), kid.getDict()); + auto p = std::make_unique(doc, pages.size() + 1, std::move(kid), kidRef.getRef(), std::move(attrs), form); + if (!p->isOk()) { + error(errSyntaxError, -1, "Failed to create page (page {0:uld})", pages.size() + 1); + return false; + } + + if (pages.size() >= std::size_t(numPages)) { + error(errSyntaxError, -1, "Page count in top-level pages object is incorrect"); + return false; + } + + auto ref = kidRef.getRef(); + pages.emplace_back(std::move(p), ref); + refPageMap.emplace(ref, pages.size()); + + kidsIdxList->back()++; + + // This should really be isDict("Pages"), but I've seen at least one + // PDF file where the /Type entry is missing. + } else if (kid.isDict()) { + attrsList.push_back(std::make_unique(attrsList.back().get(), kid.getDict())); + pagesRefList->push_back(kidRef.getRef()); + pagesList->push_back(std::move(kid)); + kidsIdxList->push_back(0); + } else { + error(errSyntaxError, -1, "Kid object (page {0:uld}) is wrong type ({1:s})", pages.size() + 1, kid.getTypeName()); + kidsIdxList->back()++; + } + + return true; } int Catalog::findPage(const Ref pageRef) { - int i; + catalogLocker(); - for (i = 0; i < getNumPages(); ++i) { - Ref *ref = getPageRef(i + 1); - if (ref != nullptr && *ref == pageRef) { - return i + 1; - } - } - return 0; + return cachePageTreeForRef(pageRef); } std::unique_ptr Catalog::findDest(const GooString *name) @@ -499,7 +555,7 @@ void Catalog::addEmbeddedFile(GooFile *file, const std::string &fileName) const bool addFile = !fileAlreadyAdded && (sameFileName || fileName < efNameI->toStr()); if (addFile) { // If the new name is smaller lexicographically than an existing file add it in its correct position - embeddedFilesNamesArray->add(Object(new GooString(fileName))); + embeddedFilesNamesArray->add(Object(std::make_unique(fileName))); embeddedFilesNamesArray->add(Object(fileSpecRef)); fileAlreadyAdded = true; } @@ -513,7 +569,7 @@ void Catalog::addEmbeddedFile(GooFile *file, const std::string &fileName) if (!fileAlreadyAdded) { // The new file is bigger lexicographically than the existing ones - embeddedFilesNamesArray->add(Object(new GooString(fileName))); + embeddedFilesNamesArray->add(Object(std::make_unique(fileName))); embeddedFilesNamesArray->add(Object(fileSpecRef)); } @@ -531,7 +587,7 @@ void Catalog::addEmbeddedFile(GooFile *file, const std::string &fileName) embeddedFileNameTree = nullptr; } -GooString *Catalog::getJS(int i) +std::string Catalog::getJS(int i) { Object obj; // getJSNameTree()->getValue(i) returns a shallow copy of the object so we @@ -543,23 +599,22 @@ GooString *Catalog::getJS(int i) } if (!obj.isDict()) { - return nullptr; + return {}; } Object obj2 = obj.dictLookup("S"); if (!obj2.isName()) { - return nullptr; + return {}; } - if (strcmp(obj2.getName(), "JavaScript")) { - return nullptr; + if (strcmp(obj2.getName(), "JavaScript") != 0) { + return {}; } obj2 = obj.dictLookup("JS"); - GooString *js = nullptr; + std::string js; if (obj2.isString()) { - js = new GooString(obj2.getString()); + js = obj2.getString()->toStr(); } else if (obj2.isStream()) { Stream *stream = obj2.getStream(); - js = new GooString(); - stream->fillGooString(js); + stream->fillString(js); } return js; } @@ -638,23 +693,8 @@ Catalog::PageLayout Catalog::getPageLayout() return pageLayout; } -NameTree::NameTree() -{ - size = 0; - length = 0; - entries = nullptr; -} - -NameTree::~NameTree() -{ - int i; - - for (i = 0; i < length; i++) { - delete entries[i]; - } - - gfree(entries); -} +NameTree::NameTree() = default; +NameTree::~NameTree() = default; NameTree::Entry::Entry(Array *array, int index) { @@ -669,42 +709,19 @@ NameTree::Entry::Entry(Array *array, int index) value = array->getNF(index + 1).copy(); } -NameTree::Entry::~Entry() { } - -void NameTree::addEntry(Entry *entry) -{ - if (length == size) { - if (length == 0) { - size = 8; - } else { - size *= 2; - } - entries = (Entry **)grealloc(entries, sizeof(Entry *) * size); - } - - entries[length] = entry; - ++length; -} - -int NameTree::Entry::cmpEntry(const void *voidEntry, const void *voidOtherEntry) -{ - Entry *entry = *(NameTree::Entry **)voidEntry; - Entry *otherEntry = *(NameTree::Entry **)voidOtherEntry; - - return entry->name.cmp(&otherEntry->name); -} +NameTree::Entry::~Entry() = default; void NameTree::init(XRef *xrefA, Object *tree) { xref = xrefA; - std::set seen; + RefRecursionChecker seen; parse(tree, seen); - if (entries && length > 0) { - qsort(entries, length, sizeof(Entry *), Entry::cmpEntry); + if (!entries.empty()) { + std::ranges::sort(entries, [](const auto &first, const auto &second) { return first->name.cmp(&second->name) < 0; }); } } -void NameTree::parse(const Object *tree, std::set &seen) +void NameTree::parse(const Object *tree, RefRecursionChecker &seen) { if (!tree->isDict()) { return; @@ -714,34 +731,24 @@ void NameTree::parse(const Object *tree, std::set &seen) Object names = tree->dictLookup("Names"); if (names.isArray()) { for (int i = 0; i < names.arrayGetLength(); i += 2) { - NameTree::Entry *entry; - - entry = new Entry(names.getArray(), i); - addEntry(entry); + auto entry = std::make_unique(names.getArray(), i); + entries.push_back(std::move(entry)); } } // root or intermediate node Ref ref; const Object kids = tree->getDict()->lookup("Kids", &ref); - if (ref != Ref::INVALID()) { - const int numObj = ref.num; - if (seen.find(numObj) != seen.end()) { - error(errSyntaxError, -1, "loop in NameTree (numObj: {0:d})", numObj); - return; - } - seen.insert(numObj); + if (!seen.insert(ref)) { + error(errSyntaxError, -1, "loop in NameTree (numObj: {0:d})", ref.num); + return; } if (kids.isArray()) { for (int i = 0; i < kids.arrayGetLength(); ++i) { const Object kid = kids.getArray()->get(i, &ref); - if (ref != Ref::INVALID()) { - const int numObj = ref.num; - if (seen.find(numObj) != seen.end()) { - error(errSyntaxError, -1, "loop in NameTree (numObj: {0:d})", numObj); - continue; - } - seen.insert(numObj); + if (!seen.insert(ref)) { + error(errSyntaxError, -1, "loop in NameTree (numObj: {0:d})", ref.num); + continue; } if (kid.isDict()) { parse(&kid, seen); @@ -750,20 +757,18 @@ void NameTree::parse(const Object *tree, std::set &seen) } } -int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry) +struct EntryGooStringComparer { - GooString *key = (GooString *)voidKey; - Entry *entry = *(NameTree::Entry **)voidEntry; - - return key->cmp(&entry->name); -} + static constexpr const GooString *get(const GooString *string) { return string; }; + static constexpr const GooString *get(const auto &entry) { return &entry->name; } + auto operator()(const auto &lhs, const auto &rhs) { return get(lhs)->cmp(get(rhs)) < 0; } +}; Object NameTree::lookup(const GooString *name) { - Entry **entry; + auto entry = std::ranges::lower_bound(entries, name, EntryGooStringComparer {}); - entry = (Entry **)bsearch(name, entries, length, sizeof(Entry *), Entry::cmp); - if (entry != nullptr) { + if (entry != entries.end() && (*entry)->name.cmp(name) == 0) { return (*entry)->value.fetch(xref); } else { error(errSyntaxError, -1, "failed to look up ({0:s})", name->c_str()); @@ -773,7 +778,7 @@ Object NameTree::lookup(const GooString *name) Object *NameTree::getValue(int index) { - if (index < length) { + if (size_t(index) < entries.size()) { return &entries[index]->value; } else { return nullptr; @@ -782,7 +787,7 @@ Object *NameTree::getValue(int index) const GooString *NameTree::getName(int index) const { - if (index < length) { + if (size_t(index) < entries.size()) { return &entries[index]->name; } else { return nullptr; @@ -834,6 +839,10 @@ int Catalog::getNumPages() { catalogLocker(); if (numPages == -1) { + if (!initPageList()) { + return 0; + } + Object catDict = xref->getCatalog(); if (!catDict.isDict()) { error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})", catDict.getTypeName()); @@ -859,9 +868,10 @@ int Catalog::getNumPages() Dict *pageDict = pagesDict.getDict(); if (pageRootRef.isRef()) { const Ref pageRef = pageRootRef.getRef(); - auto p = std::make_unique(doc, 1, std::move(pagesDict), pageRef, new PageAttrs(nullptr, pageDict), form); + auto p = std::make_unique(doc, 1, std::move(pagesDict), pageRef, std::make_unique(nullptr, pageDict), form); if (p->isOk()) { pages.emplace_back(std::move(p), pageRef); + refPageMap.emplace(pageRef, pages.size()); numPages = 1; } else { @@ -875,7 +885,13 @@ int Catalog::getNumPages() numPages = 0; } } else { - numPages = (int)obj.getNum(); + if (obj.isInt()) { + numPages = obj.getInt(); + } else if (obj.isInt64()) { + numPages = obj.getInt64(); + } else { + numPages = obj.getNum(); + } if (numPages <= 0) { error(errSyntaxError, -1, "Invalid page count {0:d}", numPages); numPages = 0; @@ -1119,6 +1135,7 @@ void Catalog::setAcroFormModified() if (acroFormRef != Ref::INVALID()) { xref->setModifiedObject(&acroForm, acroFormRef); } else { + catDict.dictSet("AcroForm", acroForm.copy()); xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() }); } } @@ -1235,3 +1252,21 @@ std::unique_ptr Catalog::getAdditionalAction(DocumentAdditionalActio } return nullptr; } + +std::unique_ptr Catalog::getOpenAction() const +{ + catalogLocker(); + const Object catDict = xref->getCatalog(); + if (!catDict.isDict()) { + return {}; + } + + const Object openActionObj = catDict.dictLookup("OpenAction"); + if (openActionObj.isArray()) { + return LinkAction::parseDest(&openActionObj); + } + if (openActionObj.isDict()) { + return LinkAction::parseAction(&openActionObj, baseURI); + } + return {}; +} diff --git a/poppler/Catalog.h b/poppler/Catalog.h index 03df89a..eba8cdc 100644 --- a/poppler/Catalog.h +++ b/poppler/Catalog.h @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Kristian Høgsberg -// Copyright (C) 2005, 2007, 2009-2011, 2013, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2007, 2009-2011, 2013, 2017-2024 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2005, 2006, 2008 Brad Hards // Copyright (C) 2007 Julien Rebetez @@ -32,6 +32,8 @@ // Copyright (C) 2020 Katarina Behrens // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright (C) 2021 RM +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Hubert Figuière // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -48,6 +50,7 @@ #include #include +#include #include class PDFDoc; @@ -78,7 +81,7 @@ class POPPLER_PRIVATE_EXPORT NameTree void init(XRef *xref, Object *tree); Object lookup(const GooString *name); - int numEntries() { return length; }; + int numEntries() { return entries.size(); }; // iterator accessor, note it returns a pointer to the internal object, do not free nor delete it Object *getValue(int i); const GooString *getName(int i) const; @@ -90,18 +93,12 @@ class POPPLER_PRIVATE_EXPORT NameTree ~Entry(); GooString name; Object value; - static int cmpEntry(const void *voidEntry, const void *voidOtherEntry); - static int cmp(const void *key, const void *entry); }; - void parse(const Object *tree, std::set &seen); - void addEntry(Entry *entry); + void parse(const Object *tree, RefRecursionChecker &seen); XRef *xref; - Entry **entries; - int size, length; // size is the number of entries in - // the array of Entry* - // length is the number of real Entry + std::vector> entries; }; //------------------------------------------------------------------------ @@ -199,7 +196,7 @@ class POPPLER_PRIVATE_EXPORT Catalog const GooString *getJSName(int i) { return getJSNameTree()->getName(i); } // Get the i'th JavaScript script (at the Document level) in the document - GooString *getJS(int i); + std::string getJS(int i); // Convert between page indices and page labels. bool labelToIndex(GooString *label, int *index); @@ -214,7 +211,7 @@ class POPPLER_PRIVATE_EXPORT Catalog void removeFormFromAcroForm(const Ref formRef); void setAcroFormModified(); - OCGs *getOptContentConfig() { return optContent; } + const OCGs *getOptContentConfig() { return optContent.get(); } int getPDFMajorVersion() const { return catalogPdfMajorVersion; } int getPDFMinorVersion() const { return catalogPdfMinorVersion; } @@ -270,6 +267,8 @@ class POPPLER_PRIVATE_EXPORT Catalog std::unique_ptr getAdditionalAction(DocumentAdditionalActionsType type); + std::unique_ptr getOpenAction() const; + private: // Get page label info. PageLabelInfo *getPageLabelInfo(); @@ -277,9 +276,10 @@ class POPPLER_PRIVATE_EXPORT Catalog PDFDoc *doc; XRef *xref; // the xref table for this PDF file std::vector, Ref>> pages; + std::unordered_map refPageMap; std::vector *pagesList; std::vector *pagesRefList; - std::vector *attrsList; + std::vector> attrsList; std::vector *kidsIdxList; Form *form; ViewerPreferences *viewerPrefs; @@ -296,14 +296,17 @@ class POPPLER_PRIVATE_EXPORT Catalog Object outline; // outline dictionary Object acroForm; // AcroForm dictionary Object viewerPreferences; // ViewerPreference dictionary - OCGs *optContent; // Optional Content groups + std::unique_ptr optContent; // Optional Content groups bool ok; // true if catalog is valid PageLabelInfo *pageLabelInfo; // info about page labels PageMode pageMode; // page mode PageLayout pageLayout; // page layout Object additionalActions; // page additional actions + bool initPageList(); // init the page list. called by cachePageTree. + bool cacheSubTree(); // called by cachePageTree. bool cachePageTree(int page); // Cache first pages. + std::size_t cachePageTreeForRef(const Ref pageRef); // Cache until . Object *findDestInTree(Object *tree, GooString *name, Object *obj); Object *getNames(); diff --git a/poppler/CertificateInfo.cc b/poppler/CertificateInfo.cc index 8b94e07..7e1e139 100644 --- a/poppler/CertificateInfo.cc +++ b/poppler/CertificateInfo.cc @@ -8,6 +8,7 @@ // Copyright 2018, 2019, 2022 Albert Astals Cid // Copyright 2018 Oliver Sander // Copyright 2020 Thorsten Behrens +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -16,32 +17,7 @@ #include #include -X509CertificateInfo::PublicKeyInfo::PublicKeyInfo() : publicKeyType(OTHERKEY), publicKeyStrength(0) { } - -X509CertificateInfo::PublicKeyInfo::PublicKeyInfo(X509CertificateInfo::PublicKeyInfo &&other) noexcept -{ - publicKey = std::move(other.publicKey); - publicKeyType = other.publicKeyType; - publicKeyStrength = other.publicKeyStrength; -} - -X509CertificateInfo::PublicKeyInfo &X509CertificateInfo::PublicKeyInfo::operator=(X509CertificateInfo::PublicKeyInfo &&other) noexcept -{ - publicKey = std::move(other.publicKey); - publicKeyType = other.publicKeyType; - publicKeyStrength = other.publicKeyStrength; - return *this; -} - -X509CertificateInfo::EntityInfo::EntityInfo() = default; - -X509CertificateInfo::EntityInfo::~EntityInfo() = default; - -X509CertificateInfo::EntityInfo::EntityInfo(X509CertificateInfo::EntityInfo &&other) noexcept = default; - -X509CertificateInfo::EntityInfo &X509CertificateInfo::EntityInfo::operator=(X509CertificateInfo::EntityInfo &&other) noexcept = default; - -X509CertificateInfo::X509CertificateInfo() : ku_extensions(KU_NONE), cert_version(-1), is_self_signed(false) { } +X509CertificateInfo::X509CertificateInfo() : ku_extensions(KU_NONE), cert_version(-1), is_qualified(false), is_self_signed(false), keyLocation(KeyLocation::Unknown) { } X509CertificateInfo::~X509CertificateInfo() = default; @@ -144,3 +120,32 @@ void X509CertificateInfo::setIsSelfSigned(bool isSelfSigned) { is_self_signed = isSelfSigned; } +KeyLocation X509CertificateInfo::getKeyLocation() const +{ + return keyLocation; +} + +void X509CertificateInfo::setKeyLocation(KeyLocation location) +{ + keyLocation = location; +} + +bool X509CertificateInfo::isQualified() const +{ + return is_qualified; +} + +void X509CertificateInfo::setQualified(bool qualified) +{ + is_qualified = qualified; +} + +CertificateType X509CertificateInfo::getCertificateType() const +{ + return certificate_type; +} + +void X509CertificateInfo::setCertificateType(CertificateType type) +{ + certificate_type = type; +} diff --git a/poppler/CertificateInfo.h b/poppler/CertificateInfo.h index b99edc8..682c1da 100644 --- a/poppler/CertificateInfo.h +++ b/poppler/CertificateInfo.h @@ -8,6 +8,7 @@ // Copyright 2018, 2019 Albert Astals Cid // Copyright 2018 Oliver Sander // Copyright 2020 Thorsten Behrens +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -40,7 +41,29 @@ enum PublicKeyType OTHERKEY }; -class POPPLER_PRIVATE_EXPORT X509CertificateInfo +/** A signing key can be located in different places + sometimes. For the user, it might be easier to pick + the key located on a card if it has some visual + indicator that it is somehow removable. + + \note a keylocation for a certificate without a private + key (cannot be used for signing) will likely be "Unknown" + */ +enum class KeyLocation +{ + Unknown, /** We don't know the location */ + Other, /** We know the location, but it is somehow not covered by this enum */ + Computer, /** The key is on this computer */ + HardwareToken /** The key is on a dedicated hardware token, either a smartcard or a dedicated usb token (e.g. gnuk, nitrokey or yubikey) */ +}; + +enum class CertificateType +{ + X509, + PGP +}; + +class POPPLER_PRIVATE_EXPORT X509CertificateInfo // TODO consider rename to just CertificateInfo { public: X509CertificateInfo(); @@ -51,26 +74,26 @@ class POPPLER_PRIVATE_EXPORT X509CertificateInfo struct PublicKeyInfo { - PublicKeyInfo(); + PublicKeyInfo() = default; - PublicKeyInfo(PublicKeyInfo &&) noexcept; - PublicKeyInfo &operator=(PublicKeyInfo &&) noexcept; + PublicKeyInfo(PublicKeyInfo &&) noexcept = default; + PublicKeyInfo &operator=(PublicKeyInfo &&) noexcept = default; PublicKeyInfo(const PublicKeyInfo &) = delete; PublicKeyInfo &operator=(const PublicKeyInfo &) = delete; GooString publicKey; - PublicKeyType publicKeyType; - unsigned int publicKeyStrength; // in bits + PublicKeyType publicKeyType = OTHERKEY; + unsigned int publicKeyStrength = 0; // in bits }; struct EntityInfo { - EntityInfo(); - ~EntityInfo(); + EntityInfo() = default; + ~EntityInfo() = default; - EntityInfo(EntityInfo &&) noexcept; - EntityInfo &operator=(EntityInfo &&) noexcept; + EntityInfo(EntityInfo &&) noexcept = default; + EntityInfo &operator=(EntityInfo &&) noexcept = default; EntityInfo(const EntityInfo &) = delete; EntityInfo &operator=(const EntityInfo &) = delete; @@ -100,6 +123,10 @@ class POPPLER_PRIVATE_EXPORT X509CertificateInfo unsigned int getKeyUsageExtensions() const; const GooString &getCertificateDER() const; bool getIsSelfSigned() const; + bool isQualified() const; + void setQualified(bool qualified); + KeyLocation getKeyLocation() const; + CertificateType getCertificateType() const; /* SETTERS */ void setVersion(int); @@ -112,6 +139,8 @@ class POPPLER_PRIVATE_EXPORT X509CertificateInfo void setKeyUsageExtensions(unsigned int); void setCertificateDER(const GooString &); void setIsSelfSigned(bool); + void setKeyLocation(KeyLocation location); + void setCertificateType(CertificateType type); private: EntityInfo issuer_info; @@ -123,7 +152,10 @@ class POPPLER_PRIVATE_EXPORT X509CertificateInfo GooString cert_nick; unsigned int ku_extensions; int cert_version; + bool is_qualified; bool is_self_signed; + KeyLocation keyLocation; + CertificateType certificate_type = CertificateType::X509; }; #endif diff --git a/poppler/CharCodeToUnicode.cc b/poppler/CharCodeToUnicode.cc index cd00937..53893ed 100644 --- a/poppler/CharCodeToUnicode.cc +++ b/poppler/CharCodeToUnicode.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2006, 2008-2010, 2012, 2018-2022 Albert Astals Cid +// Copyright (C) 2006, 2008-2010, 2012, 2018-2022, 2024 Albert Astals Cid // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2007 Koji Otani // Copyright (C) 2008 Michael Vrable @@ -28,6 +28,7 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -38,6 +39,7 @@ #include #include +#include #include "goo/glibc.h" #include "goo/gmem.h" #include "goo/gfile.h" @@ -51,13 +53,6 @@ //------------------------------------------------------------------------ -struct CharCodeToUnicodeString -{ - CharCode c; - Unicode *u; - int len; -}; - //------------------------------------------------------------------------ static int getCharFromString(void *data) @@ -105,38 +100,36 @@ static const int hexCharVals[256] = { // error. static bool parseHex(const char *s, int len, unsigned int *val) { - int i, x; + int i, x, v = 0; - *val = 0; for (i = 0; i < len; ++i) { x = hexCharVals[s[i] & 0xff]; if (x < 0) { + *val = 0; return false; } - *val = (*val << 4) + x; + v = (v << 4) + x; } + *val = v; return true; } //------------------------------------------------------------------------ -CharCodeToUnicode *CharCodeToUnicode::makeIdentityMapping() +std::unique_ptr CharCodeToUnicode::makeIdentityMapping() { - CharCodeToUnicode *ctu = new CharCodeToUnicode(); + auto ctu = std::make_unique(); ctu->isIdentity = true; - ctu->mapLen = 1; - ctu->map = (Unicode *)gmallocn(ctu->mapLen, sizeof(Unicode)); + ctu->map.resize(1, 0); return ctu; } -CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(const char *fileName, const GooString *collection) +std::unique_ptr CharCodeToUnicode::parseCIDToUnicode(const char *fileName, const GooString *collection) { FILE *f; - Unicode *mapA; - CharCode size, mapLenA; + CharCode size; char buf[64]; Unicode u; - CharCodeToUnicode *ctu; if (!(f = openFile(fileName, "r"))) { error(errIO, -1, "Couldn't open cidToUnicode file '{0:s}'", fileName); @@ -144,13 +137,14 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(const char *fileName, co } size = 32768; - mapA = (Unicode *)gmallocn(size, sizeof(Unicode)); - mapLenA = 0; + std::vector mapA; + mapA.resize(size, 0); + CharCode mapLenA = 0; while (getLine(buf, sizeof(buf), f)) { if (mapLenA == size) { size *= 2; - mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode)); + mapA.resize(size); } if (sscanf(buf, "%x", &u) == 1) { mapA[mapLenA] = u; @@ -161,126 +155,34 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(const char *fileName, co ++mapLenA; } fclose(f); + mapA.resize(mapLenA); - ctu = new CharCodeToUnicode(collection->toStr(), mapA, mapLenA, true, nullptr, 0, 0); - gfree(mapA); - return ctu; + return std::make_unique(collection->toStr(), std::move(mapA), std::vector {}); } -CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(const GooString *fileName) +std::unique_ptr CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) { - FILE *f; - Unicode *mapA; - CharCodeToUnicodeString *sMapA; - CharCode size, oldSize, len, sMapSizeA, sMapLenA; - char buf[256]; - char *tok; - Unicode u0; - int uBufSize = 8; - Unicode *uBuf = (Unicode *)gmallocn(uBufSize, sizeof(Unicode)); - CharCodeToUnicode *ctu; - int line, n, i; - char *tokptr; - - if (!(f = openFile(fileName->c_str(), "r"))) { - gfree(uBuf); - error(errIO, -1, "Couldn't open unicodeToUnicode file '{0:t}'", fileName); - return nullptr; - } - - size = 4096; - mapA = (Unicode *)gmallocn(size, sizeof(Unicode)); - memset(mapA, 0, size * sizeof(Unicode)); - len = 0; - sMapA = nullptr; - sMapSizeA = sMapLenA = 0; - - line = 0; - while (getLine(buf, sizeof(buf), f)) { - ++line; - if (!(tok = strtok_r(buf, " \t\r\n", &tokptr)) || !parseHex(tok, strlen(tok), &u0)) { - error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); - continue; - } - n = 0; - while ((tok = strtok_r(nullptr, " \t\r\n", &tokptr))) { - if (n >= uBufSize) { - uBufSize += 8; - uBuf = (Unicode *)greallocn(uBuf, uBufSize, sizeof(Unicode)); - } - if (!parseHex(tok, strlen(tok), &uBuf[n])) { - error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); - break; - } - ++n; - } - if (n < 1) { - error(errSyntaxWarning, -1, "Bad line ({0:d}) in unicodeToUnicode file '{1:t}'", line, fileName); - continue; - } - if (u0 >= size) { - oldSize = size; - while (u0 >= size) { - size *= 2; - } - mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode)); - memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode)); - } - if (n == 1) { - mapA[u0] = uBuf[0]; - } else { - mapA[u0] = 0; - if (sMapLenA == sMapSizeA) { - sMapSizeA += 16; - sMapA = (CharCodeToUnicodeString *)greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString)); - } - sMapA[sMapLenA].c = u0; - sMapA[sMapLenA].u = (Unicode *)gmallocn(n, sizeof(Unicode)); - for (i = 0; i < n; ++i) { - sMapA[sMapLenA].u[i] = uBuf[i]; - } - sMapA[sMapLenA].len = n; - ++sMapLenA; - } - if (u0 >= len) { - len = u0 + 1; - } - } - fclose(f); - - ctu = new CharCodeToUnicode(fileName->toStr(), mapA, len, true, sMapA, sMapLenA, sMapSizeA); - gfree(mapA); - gfree(uBuf); - return ctu; + std::vector data(toUnicode, toUnicode + 256); + return std::make_unique(std::optional {}, std::move(data), std::vector {}); } -CharCodeToUnicode *CharCodeToUnicode::make8BitToUnicode(Unicode *toUnicode) +std::unique_ptr CharCodeToUnicode::parseCMap(const GooString *buf, int nBits) { - return new CharCodeToUnicode({}, toUnicode, 256, true, nullptr, 0, 0); -} - -CharCodeToUnicode *CharCodeToUnicode::parseCMap(const GooString *buf, int nBits) -{ - CharCodeToUnicode *ctu; - - ctu = new CharCodeToUnicode(std::optional()); + auto ctu = std::make_unique(std::optional {}); const char *p = buf->c_str(); - if (!ctu->parseCMap1(&getCharFromString, &p, nBits)) { - delete ctu; + if (!ctu->parseCMap1(&getCharFromString, static_cast(&p), nBits)) { return nullptr; } return ctu; } -CharCodeToUnicode *CharCodeToUnicode::parseCMapFromFile(const GooString *fileName, int nBits) +std::unique_ptr CharCodeToUnicode::parseCMapFromFile(const GooString *fileName, int nBits) { - CharCodeToUnicode *ctu; FILE *f; - ctu = new CharCodeToUnicode(std::optional()); + auto ctu = std::make_unique(std::optional()); if ((f = globalParams->findToUnicodeFile(fileName))) { - if (!ctu->parseCMap1(&getCharFromFile, f, nBits)) { - delete ctu; + if (!ctu->parseCMap1(&getCharFromFile, static_cast(f), nBits)) { fclose(f); return nullptr; } @@ -293,7 +195,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseCMapFromFile(const GooString *fileNam void CharCodeToUnicode::mergeCMap(const GooString *buf, int nBits) { const char *p = buf->c_str(); - parseCMap1(&getCharFromString, &p, nBits); + parseCMap1(&getCharFromString, static_cast(&p), nBits); } bool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int nBits) @@ -303,7 +205,6 @@ bool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int n int n1, n2, n3; CharCode i; CharCode maxCode, code1, code2; - GooString *name; FILE *f; bool ok = false; @@ -313,16 +214,15 @@ bool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int n while (pst->getToken(tok2, sizeof(tok2), &n2)) { if (!strcmp(tok2, "usecmap")) { if (tok1[0] == '/') { - name = new GooString(tok1 + 1); - if ((f = globalParams->findToUnicodeFile(name))) { + GooString name { tok1 + 1 }; + if ((f = globalParams->findToUnicodeFile(&name))) { if (parseCMap1(&getCharFromFile, f, nBits)) { ok = true; } fclose(f); } else { - error(errSyntaxError, -1, "Couldn't find ToUnicode CMap file for '{0:t}'", name); + error(errSyntaxError, -1, "Couldn't find ToUnicode CMap file for '{0:s}'", name.c_str()); } - delete name; } pst->getToken(tok1, sizeof(tok1), &n1); } else if (!strcmp(tok2, "beginbfchar")) { @@ -477,7 +377,6 @@ bool CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data, int n void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) { - CharCode oldLen, i; Unicode u; int j; @@ -486,20 +385,17 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) // (I've seen CMaps with mappings for .) return; } - if (code >= mapLen) { - oldLen = mapLen; - mapLen = mapLen ? 2 * mapLen : 256; - if (code >= mapLen) { - mapLen = (code + 256) & ~255; + if (code >= map.size()) { + size_t oldLen = map.size(); + auto newLen = oldLen ? 2 * oldLen : 256; + if (code >= newLen) { + newLen = (code + 256) & ~255; } - if (unlikely(code >= mapLen)) { + if (unlikely(code >= newLen)) { error(errSyntaxWarning, -1, "Illegal code value in CharCodeToUnicode::addMapping"); return; } else { - map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode)); - for (i = oldLen; i < mapLen; ++i) { - map[i] = 0; - } + map.resize(newLen, 0); } } if (n <= 4) { @@ -512,115 +408,56 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n, int offset) map[code] = 0xfffd; } } else { - if (sMapLen >= sMapSize) { - sMapSize = sMapSize + 16; - sMap = (CharCodeToUnicodeString *)greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString)); - } map[code] = 0; - sMap[sMapLen].c = code; int utf16Len = n / 4; - Unicode *utf16 = (Unicode *)gmallocn(utf16Len, sizeof(Unicode)); + std::vector utf16(utf16Len); + utf16.resize(utf16Len); for (j = 0; j < utf16Len; ++j) { if (!parseHex(uStr + j * 4, 4, &utf16[j])) { - gfree(utf16); error(errSyntaxWarning, -1, "Illegal entry in ToUnicode CMap"); return; } } utf16[utf16Len - 1] += offset; - sMap[sMapLen].len = UTF16toUCS4(utf16, utf16Len, &sMap[sMapLen].u); - gfree(utf16); - ++sMapLen; + sMap.push_back({ code, UTF16toUCS4(utf16) }); } } void CharCodeToUnicode::addMappingInt(CharCode code, Unicode u) { - CharCode oldLen, i; - if (code > 0xffffff) { // This is an arbitrary limit to avoid integer overflow issues. // (I've seen CMaps with mappings for .) return; } - if (code >= mapLen) { - oldLen = mapLen; - mapLen = mapLen ? 2 * mapLen : 256; - if (code >= mapLen) { - mapLen = (code + 256) & ~255; - } - map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode)); - for (i = oldLen; i < mapLen; ++i) { - map[i] = 0; + if (code >= map.size()) { + size_t oldLen = map.size(); + size_t newLen = oldLen ? 2 * oldLen : 256; + if (code >= newLen) { + newLen = (code + 256) & ~255; } + map.resize(newLen, 0); } map[code] = u; } -CharCodeToUnicode::CharCodeToUnicode() +CharCodeToUnicode::CharCodeToUnicode(PrivateTag) { - map = nullptr; - mapLen = 0; - sMap = nullptr; - sMapLen = sMapSize = 0; - refCnt = 1; isIdentity = false; } -CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA) : tag(tagA) +CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA, PrivateTag) : tag(tagA) { - CharCode i; - - mapLen = 256; - map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); - for (i = 0; i < mapLen; ++i) { - map[i] = 0; - } - sMap = nullptr; - sMapLen = sMapSize = 0; - refCnt = 1; + map.resize(256, 0); isIdentity = false; } - -CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA) : tag(tagA) +CharCodeToUnicode::CharCodeToUnicode(const std::optional &tagA, std::vector &&mapA, std::vector &&sMapA, PrivateTag) : tag(tagA) { - mapLen = mapLenA; - if (copyMap) { - map = (Unicode *)gmallocn(mapLen, sizeof(Unicode)); - memcpy(map, mapA, mapLen * sizeof(Unicode)); - } else { - map = mapA; - } - sMap = sMapA; - sMapLen = sMapLenA; - sMapSize = sMapSizeA; - refCnt = 1; + map = std::move(mapA); + sMap = std::move(sMapA); isIdentity = false; } -CharCodeToUnicode::~CharCodeToUnicode() -{ - gfree(map); - if (sMap) { - for (int i = 0; i < sMapLen; ++i) { - gfree(sMap[i].u); - } - gfree(sMap); - } -} - -void CharCodeToUnicode::incRefCnt() -{ - ++refCnt; -} - -void CharCodeToUnicode::decRefCnt() -{ - if (--refCnt == 0) { - delete this; - } -} - bool CharCodeToUnicode::match(const GooString *tagA) { return tag && tag == tagA->toStr(); @@ -628,36 +465,35 @@ bool CharCodeToUnicode::match(const GooString *tagA) void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) { - int i, j; + size_t i; + int j; - if (!map || isIdentity) { + if (map.empty() || isIdentity) { return; } if (len == 1) { map[c] = u[0]; } else { - for (i = 0; i < sMapLen; ++i) { + std::optional> element; + for (i = 0; i < sMap.size(); ++i) { if (sMap[i].c == c) { - gfree(sMap[i].u); + sMap[i].u.clear(); + element = std::ref(sMap[i]); break; } } - if (i == sMapLen) { - if (sMapLen == sMapSize) { - sMapSize += 8; - sMap = (CharCodeToUnicodeString *)greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString)); - } - ++sMapLen; + if (!element) { + sMap.emplace_back(); + element = std::ref(sMap.back()); } map[c] = 0; - sMap[i].c = c; - sMap[i].len = len; - sMap[i].u = (Unicode *)gmallocn(len, sizeof(Unicode)); + element->get().c = c; + element->get().u.reserve(len); for (j = 0; j < len; ++j) { if (UnicodeIsValid(u[j])) { - sMap[i].u[j] = u[j]; + element->get().u.push_back(u[j]); } else { - sMap[i].u[j] = 0xfffd; + element->get().u.push_back(0xfffd); } } } @@ -665,24 +501,23 @@ void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) int CharCodeToUnicode::mapToUnicode(CharCode c, Unicode const **u) const { - int i; - if (isIdentity) { - map[0] = (Unicode)c; - *u = map; + auto that = const_cast(this); + that->map[0] = (Unicode)c; + *u = map.data(); return 1; } - if (c >= mapLen) { + if (c >= map.size()) { return 0; } if (map[c]) { *u = &map[c]; return 1; } - for (i = sMapLen - 1; i >= 0; --i) { // in reverse so CMap takes precedence - if (sMap[i].c == c) { - *u = sMap[i].u; - return sMap[i].len; + for (auto i = sMap.size(); i > 0; --i) { // in reverse so CMap takes precedence + if (sMap[i - 1].c == c) { + *u = sMap[i - 1].u.data(); + return sMap[i - 1].u.size(); } } return 0; @@ -696,7 +531,7 @@ int CharCodeToUnicode::mapToCharCode(const Unicode *u, CharCode *c, int usize) c *c = (CharCode)*u; return 1; } - for (CharCode i = 0; i < mapLen; i++) { + for (CharCode i = 0; i < map.size(); i++) { if (map[i] == *u) { *c = i; return 1; @@ -704,24 +539,24 @@ int CharCodeToUnicode::mapToCharCode(const Unicode *u, CharCode *c, int usize) c } *c = 'x'; } else { - int i, j; + size_t j; // for each entry in the sMap - for (i = 0; i < sMapLen; i++) { + for (const auto &element : sMap) { // if the entry's unicode length isn't the same are usize, the strings // are obviously different - if (sMap[i].len != usize) { + if (element.u.size() != size_t(usize)) { continue; } // compare the string char by char - for (j = 0; j < sMap[i].len; j++) { - if (sMap[i].u[j] != u[j]) { + for (j = 0; j < element.u.size(); j++) { + if (element.u[j] != u[j]) { break; } } // we have the same strings - if (j == sMap[i].len) { - *c = sMap[i].c; + if (j == element.u.size()) { + *c = element.c; return 1; } } @@ -731,62 +566,29 @@ int CharCodeToUnicode::mapToCharCode(const Unicode *u, CharCode *c, int usize) c //------------------------------------------------------------------------ -CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) -{ - int i; +CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) : size(sizeA) { } - size = sizeA; - cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *)); - for (i = 0; i < size; ++i) { - cache[i] = nullptr; - } -} +CharCodeToUnicodeCache::~CharCodeToUnicodeCache() = default; -CharCodeToUnicodeCache::~CharCodeToUnicodeCache() +std::shared_ptr CharCodeToUnicodeCache::getCharCodeToUnicode(const GooString *tag) { - int i; - - for (i = 0; i < size; ++i) { - if (cache[i]) { - cache[i]->decRefCnt(); - } - } - gfree(cache); -} - -CharCodeToUnicode *CharCodeToUnicodeCache::getCharCodeToUnicode(const GooString *tag) -{ - CharCodeToUnicode *ctu; - int i, j; - - if (cache[0] && cache[0]->match(tag)) { - cache[0]->incRefCnt(); - return cache[0]; - } - for (i = 1; i < size; ++i) { - if (cache[i] && cache[i]->match(tag)) { - ctu = cache[i]; - for (j = i; j >= 1; --j) { - cache[j] = cache[j - 1]; + for (auto it = cache.begin(); it != cache.end(); ++it) { + if ((*it)->match(tag)) { + if (it != cache.begin()) { + auto item = std::move(*it); + cache.erase(it); + cache.push_front(item); } - cache[0] = ctu; - ctu->incRefCnt(); - return ctu; + return cache[0]; } } return nullptr; } -void CharCodeToUnicodeCache::add(CharCodeToUnicode *ctu) +void CharCodeToUnicodeCache::add(std::shared_ptr ctu) { - int i; - - if (cache[size - 1]) { - cache[size - 1]->decRefCnt(); - } - for (i = size - 1; i >= 1; --i) { - cache[i] = cache[i - 1]; + if (size == cache.size()) { + cache.pop_back(); } - cache[0] = ctu; - ctu->incRefCnt(); + cache.push_front(std::move(ctu)); } diff --git a/poppler/CharCodeToUnicode.h b/poppler/CharCodeToUnicode.h index 596d44d..7cf0404 100644 --- a/poppler/CharCodeToUnicode.h +++ b/poppler/CharCodeToUnicode.h @@ -17,11 +17,12 @@ // // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2007 Koji Otani -// Copyright (C) 2008, 2011, 2012, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2008, 2011, 2012, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -31,13 +32,15 @@ #ifndef CHARCODETOUNICODE_H #define CHARCODETOUNICODE_H -#include #include +#include +#include +#include +#include #include "poppler-config.h" #include "CharTypes.h" -struct CharCodeToUnicodeString; class GooString; //------------------------------------------------------------------------ @@ -45,42 +48,42 @@ class GooString; class CharCodeToUnicode { friend class UnicodeToCharCode; + struct PrivateTag + { + }; + struct CharCodeToUnicodeString + { + CharCode c; + std::vector u; + }; public: // Create an identity mapping (Unicode = CharCode). - static CharCodeToUnicode *makeIdentityMapping(); + static std::unique_ptr makeIdentityMapping(); // Read the CID-to-Unicode mapping for from the file // specified by . Sets the initial reference count to 1. // Returns NULL on failure. - static CharCodeToUnicode *parseCIDToUnicode(const char *fileName, const GooString *collection); - - // Create a Unicode-to-Unicode mapping from the file specified by - // . Sets the initial reference count to 1. Returns NULL - // on failure. - static CharCodeToUnicode *parseUnicodeToUnicode(const GooString *fileName); + static std::unique_ptr parseCIDToUnicode(const char *fileName, const GooString *collection); // Create the CharCode-to-Unicode mapping for an 8-bit font. // is an array of 256 Unicode indexes. Sets the initial // reference count to 1. - static CharCodeToUnicode *make8BitToUnicode(Unicode *toUnicode); + static std::unique_ptr make8BitToUnicode(Unicode *toUnicode); // Parse a ToUnicode CMap for an 8- or 16-bit font. - static CharCodeToUnicode *parseCMap(const GooString *buf, int nBits); - static CharCodeToUnicode *parseCMapFromFile(const GooString *fileName, int nBits); + static std::unique_ptr parseCMap(const GooString *buf, int nBits); + static std::unique_ptr parseCMapFromFile(const GooString *fileName, int nBits); // Parse a ToUnicode CMap for an 8- or 16-bit font, merging it into // . void mergeCMap(const GooString *buf, int nBits); - ~CharCodeToUnicode(); + ~CharCodeToUnicode() = default; CharCodeToUnicode(const CharCodeToUnicode &) = delete; CharCodeToUnicode &operator=(const CharCodeToUnicode &) = delete; - void incRefCnt(); - void decRefCnt(); - // Return true if this mapping matches the specified . bool match(const GooString *tagA); @@ -95,24 +98,18 @@ class CharCodeToUnicode // Map a Unicode to CharCode. int mapToCharCode(const Unicode *u, CharCode *c, int usize) const; - // Return the mapping's length, i.e., one more than the max char - // code supported by the mapping. - CharCode getLength() const { return mapLen; } + explicit CharCodeToUnicode(PrivateTag t = {}); + explicit CharCodeToUnicode(const std::optional &tagA, PrivateTag t = {}); + CharCodeToUnicode(const std::optional &tagA, std::vector &&mapA, std::vector &&sMapA, PrivateTag t = {}); private: bool parseCMap1(int (*getCharFunc)(void *), void *data, int nBits); void addMapping(CharCode code, char *uStr, int n, int offset); void addMappingInt(CharCode code, Unicode u); - CharCodeToUnicode(); - explicit CharCodeToUnicode(const std::optional &tagA); - CharCodeToUnicode(const std::optional &tagA, Unicode *mapA, CharCode mapLenA, bool copyMap, CharCodeToUnicodeString *sMapA, int sMapLenA, int sMapSizeA); const std::optional tag; - Unicode *map; - CharCode mapLen; - CharCodeToUnicodeString *sMap; - int sMapLen, sMapSize; - std::atomic_int refCnt; + std::vector map; + std::vector sMap; bool isIdentity; }; @@ -127,17 +124,16 @@ class CharCodeToUnicodeCache CharCodeToUnicodeCache(const CharCodeToUnicodeCache &) = delete; CharCodeToUnicodeCache &operator=(const CharCodeToUnicodeCache &) = delete; - // Get the CharCodeToUnicode object for . Increments its - // reference count; there will be one reference for the cache plus - // one for the caller of this function. Returns NULL on failure. - CharCodeToUnicode *getCharCodeToUnicode(const GooString *tag); + // Get the CharCodeToUnicode object for . + // Returns NULL on failure. + std::shared_ptr getCharCodeToUnicode(const GooString *tag); // Insert into the cache, in the most-recently-used position. - void add(CharCodeToUnicode *ctu); + void add(std::shared_ptr ctu); private: - CharCodeToUnicode **cache; - int size; + size_t size; + std::deque> cache; }; #endif diff --git a/poppler/CryptoSignBackend.cc b/poppler/CryptoSignBackend.cc new file mode 100644 index 0000000..0016f6f --- /dev/null +++ b/poppler/CryptoSignBackend.cc @@ -0,0 +1,150 @@ +//======================================================================== +// +// CryptoSignBackend.cc +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2024 Albert Astals Cid +//======================================================================== +#include "CryptoSignBackend.h" +#include "config.h" +#ifdef ENABLE_GPGME +# include "GPGMECryptoSignBackend.h" +#endif +#ifdef ENABLE_NSS3 +# include "NSSCryptoSignBackend.h" +#endif + +namespace CryptoSign { + +SignatureType signatureTypeFromString(std::string_view data) +{ + if (data == std::string_view("ETSI.CAdES.detached")) { + return CryptoSign::SignatureType::ETSI_CAdES_detached; + } else if (data == std::string_view("adbe.pkcs7.detached")) { + return CryptoSign::SignatureType::adbe_pkcs7_detached; + } else if (data == std::string_view("adbe.pkcs7.sha1")) { + return CryptoSign::SignatureType::adbe_pkcs7_sha1; + } else if (data == std::string_view("g10c.pgp.signature.detached")) { + return CryptoSign::SignatureType::g10c_pgp_signature_detached; + } + return CryptoSign::SignatureType::unknown_signature_type; +} + +std::string toStdString(SignatureType type) +{ + switch (type) { + case CryptoSign::SignatureType::unsigned_signature_field: + return "Unsigned"; + case CryptoSign::SignatureType::unknown_signature_type: + return "Unknown"; + case CryptoSign::SignatureType::ETSI_CAdES_detached: + return "ETSI.CAdES.detached"; + case CryptoSign::SignatureType::adbe_pkcs7_detached: + return "adbe.pkcs7.detached"; + case CryptoSign::SignatureType::adbe_pkcs7_sha1: + return "adbe.pkcs7.sha1"; + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + return "g10c.pgp.signature.detached"; + } + return "Unhandled"; +} + +void Factory::setPreferredBackend(CryptoSign::Backend::Type backend) +{ + preferredBackend = backend; +} +static std::string_view toStringView(const char *str) +{ + if (str) { + return std::string_view(str); + } + return {}; +} + +std::optional Factory::typeFromString(std::string_view string) +{ + if (string.empty()) { + return std::nullopt; + } + if ("GPG" == string) { + return Backend::Type::GPGME; + } + if ("NSS" == string) { + return Backend::Type::NSS3; + } + return std::nullopt; +} + +std::optional Factory::getActive() +{ + if (preferredBackend) { + return preferredBackend; + } + static auto backendFromEnvironment = typeFromString(toStringView(getenv("POPPLER_SIGNATURE_BACKEND"))); + if (backendFromEnvironment) { + return backendFromEnvironment; + } + static auto backendFromCompiledDefault = typeFromString(toStringView(DEFAULT_SIGNATURE_BACKEND)); + if (backendFromCompiledDefault) { + return backendFromCompiledDefault; + } + + return std::nullopt; +} +static std::vector createAvailableBackends() +{ + std::vector backends; +#ifdef ENABLE_NSS3 + backends.push_back(Backend::Type::NSS3); +#endif +#ifdef ENABLE_GPGME + if (GpgSignatureBackend::hasSufficientVersion()) { + backends.push_back(Backend::Type::GPGME); + } +#endif + return backends; +} +std::vector Factory::getAvailable() +{ + static std::vector availableBackends = createAvailableBackends(); + return availableBackends; +} +std::unique_ptr Factory::createActive() +{ + auto active = getActive(); + if (active) { + return create(active.value()); + } + return nullptr; +} +std::unique_ptr CryptoSign::Factory::create(Backend::Type backend) +{ + switch (backend) { + case Backend::Type::NSS3: +#ifdef ENABLE_NSS3 + return std::make_unique(); +#else + return nullptr; +#endif + case Backend::Type::GPGME: { +#ifdef ENABLE_GPGME + return std::make_unique(); +#else + return nullptr; +#endif + } + } + return nullptr; +} +/// backend specific settings + +// Android build wants some methods out of line in the interfaces +Backend::~Backend() = default; +SigningInterface::~SigningInterface() = default; +VerificationInterface::~VerificationInterface() = default; + +std::optional Factory::preferredBackend = std::nullopt; + +} // namespace Signature; diff --git a/poppler/CryptoSignBackend.h b/poppler/CryptoSignBackend.h new file mode 100644 index 0000000..00c3996 --- /dev/null +++ b/poppler/CryptoSignBackend.h @@ -0,0 +1,131 @@ +//======================================================================== +// +// CryptoSignBackend.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#ifndef SIGNATUREBACKEND_H +#define SIGNATUREBACKEND_H + +#include +#include +#include +#include +#include +#include +#include "HashAlgorithm.h" +#include "CertificateInfo.h" +#include "SignatureInfo.h" +#include "poppler_private_export.h" + +namespace CryptoSign { + +enum class SignatureType +{ + adbe_pkcs7_sha1, + adbe_pkcs7_detached, + ETSI_CAdES_detached, + g10c_pgp_signature_detached, + unknown_signature_type, + unsigned_signature_field +}; + +SignatureType signatureTypeFromString(std::string_view data); + +std::string toStdString(SignatureType type); + +// experiments seems to say that this is a bit above +// what we have seen in the wild, and much larger than +// what we have managed to get nss and gpgme to create. +static const int maxSupportedSignatureSize = 10000; + +enum class SigningError +{ + GenericError /** Unclassified error*/, + InternalError /** Some sort of internal error. This is likely coming from an actual bug in the code*/, + WriteFailed /**Some sort of IO error, missing write permissions or ...*/, + UserCancelled /**User cancelled the action*/, + KeyMissing, /**The key/certificate not specified*/ + BadPassphrase, /** Bad passphrase */ + +}; + +// Classes to help manage signature backends + +class VerificationInterface +{ +public: + virtual void addData(unsigned char *data_block, int data_len) = 0; + virtual SignatureValidationStatus validateSignature() = 0; + virtual std::chrono::system_clock::time_point getSigningTime() const = 0; + virtual std::string getSignerName() const = 0; + virtual std::string getSignerSubjectDN() const = 0; + virtual HashAlgorithm getHashAlgorithm() const = 0; + + // Blocking if doneCallback to validateCertificateAsync has not yet been called + virtual CertificateValidationStatus validateCertificateResult() = 0; + virtual void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function &doneCallback) = 0; + virtual std::unique_ptr getCertificateInfo() const = 0; + virtual ~VerificationInterface(); + VerificationInterface() = default; + VerificationInterface(const VerificationInterface &other) = delete; + VerificationInterface &operator=(const VerificationInterface &other) = delete; +}; + +class SigningInterface +{ +public: + virtual void addData(unsigned char *data_block, int data_len) = 0; + virtual SignatureType signatureType() const = 0; + virtual std::unique_ptr getCertificateInfo() const = 0; + virtual std::variant, SigningError> signDetached(const std::string &password) = 0; + virtual ~SigningInterface(); + SigningInterface() = default; + SigningInterface(const SigningInterface &other) = delete; + SigningInterface &operator=(const SigningInterface &other) = delete; +}; + +class Backend +{ +public: + enum class Type + { + NSS3, + GPGME + }; + virtual std::unique_ptr createVerificationHandler(std::vector &&pkcs7, SignatureType type) = 0; + virtual std::unique_ptr createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) = 0; + virtual std::vector> getAvailableSigningCertificates() = 0; + virtual ~Backend(); + Backend() = default; + Backend(const Backend &other) = delete; + Backend &operator=(const Backend &other) = delete; +}; + +class POPPLER_PRIVATE_EXPORT Factory +{ +public: + // Sets the user preferred backend + static void setPreferredBackend(Backend::Type backend); + // Gets the current active backend + // prioritized from 1) setPreferredBackend, + // 2) POPPLER_SIGNATURE_BACKEND + // 3) Compiled in default + static std::optional getActive(); + static std::vector getAvailable(); + static std::unique_ptr createActive(); + static std::unique_ptr create(Backend::Type); + static std::optional typeFromString(std::string_view string); + Factory() = delete; + /// backend specific settings + +private: + static std::optional preferredBackend; +}; + +} + +#endif // SIGNATUREBACKEND_H diff --git a/poppler/CurlCachedFile.cc b/poppler/CurlCachedFile.cc index 4ec0e20..6b5cbb9 100644 --- a/poppler/CurlCachedFile.cc +++ b/poppler/CurlCachedFile.cc @@ -7,6 +7,7 @@ // Copyright 2009 Stefan Thomas // Copyright 2010, 2011 Hib Eris // Copyright 2010, 2019, 2021, 2022 Albert Astals Cid +// Copyright 2024 Oliver Sander // //======================================================================== @@ -75,12 +76,12 @@ int CurlCachedFileLoader::load(const std::vector &ranges, CachedFileW fromByte = bRange.offset; toByte = fromByte + bRange.length - 1; - const std::unique_ptr range = GooString::format("{0:ulld}-{1:ulld}", fromByte, toByte); + const std::string range = GooString::format("{0:ulld}-{1:ulld}", fromByte, toByte); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, load_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, writer); - curl_easy_setopt(curl, CURLOPT_RANGE, range->c_str()); + curl_easy_setopt(curl, CURLOPT_RANGE, range.c_str()); r = curl_easy_perform(curl); curl_easy_reset(curl); diff --git a/poppler/CurlPDFDocBuilder.cc b/poppler/CurlPDFDocBuilder.cc index 57ae7a5..2c3a16a 100644 --- a/poppler/CurlPDFDocBuilder.cc +++ b/poppler/CurlPDFDocBuilder.cc @@ -5,8 +5,9 @@ // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris -// Copyright 2010, 2017, 2022 Albert Astals Cid +// Copyright 2010, 2017, 2022, 2024 Albert Astals Cid // Copyright 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -22,18 +23,18 @@ // CurlPDFDocBuilder //------------------------------------------------------------------------ -std::unique_ptr CurlPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) +std::unique_ptr CurlPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword) { CachedFile *cachedFile = new CachedFile(new CurlCachedFileLoader(uri.toStr())); if (cachedFile->getLength() == ((unsigned int)-1)) { cachedFile->decRefCnt(); - return PDFDoc::ErrorPDFDoc(errOpenFile, std::unique_ptr(uri.copy())); + return PDFDoc::ErrorPDFDoc(errOpenFile, uri.copy()); } BaseStream *str = new CachedFileStream(cachedFile, 0, false, cachedFile->getLength(), Object(objNull)); - return std::make_unique(str, ownerPassword, userPassword, guiDataA); + return std::make_unique(str, ownerPassword, userPassword); } bool CurlPDFDocBuilder::supports(const GooString &uri) diff --git a/poppler/CurlPDFDocBuilder.h b/poppler/CurlPDFDocBuilder.h index f1106b3..e91691a 100644 --- a/poppler/CurlPDFDocBuilder.h +++ b/poppler/CurlPDFDocBuilder.h @@ -7,6 +7,7 @@ // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -25,7 +26,7 @@ class CurlPDFDocBuilder : public PDFDocBuilder { public: - std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; + std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}) override; bool supports(const GooString &uri) override; }; diff --git a/poppler/DCTStream.cc b/poppler/DCTStream.cc index 70dd0c7..708f484 100644 --- a/poppler/DCTStream.cc +++ b/poppler/DCTStream.cc @@ -5,7 +5,7 @@ // This file is licensed under the GPLv2 or later // // Copyright 2005 Jeff Muizelaar -// Copyright 2005-2010, 2012, 2017, 2020-2022 Albert Astals Cid +// Copyright 2005-2010, 2012, 2017, 2020-2023 Albert Astals Cid // Copyright 2009 Ryszard Trojnacki // Copyright 2010 Carlos Garcia Campos // Copyright 2011 Daiki Ueno @@ -13,6 +13,8 @@ // Copyright 2012, 2013 Thomas Freitag // Copyright 2017 Adrian Johnson // Copyright 2020 Lluís Batlle i Rossell +// Copyright 2025 Nelson Benítez León +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -33,10 +35,10 @@ static boolean str_fill_input_buffer(j_decompress_ptr cinfo) } else { c = src->str->getChar(); } + src->buffer = c; + src->pub.next_input_byte = &src->buffer; + src->pub.bytes_in_buffer = 1; if (c != EOF) { - src->buffer = c; - src->pub.next_input_byte = &src->buffer; - src->pub.bytes_in_buffer = 1; return TRUE; } else { return FALSE; @@ -114,11 +116,11 @@ void DCTStream::init() row_buffer = nullptr; } -void DCTStream::reset() +bool DCTStream::reset() { int row_stride; - str->reset(); + bool success = str->reset(); if (row_buffer) { jpeg_destroy_decompress(&cinfo); @@ -137,7 +139,7 @@ void DCTStream::reset() c = str->getChar(); if (c == -1) { error(errSyntaxError, -1, "Could not find start of jpeg data"); - return; + return false; } if (c != 0xFF) { c = 0; @@ -187,6 +189,8 @@ void DCTStream::reset() row_buffer = cinfo.mem->alloc_sarray((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); } } + + return success; } bool DCTStream::readLine() @@ -246,17 +250,17 @@ int DCTStream::lookChar() return *current; } -GooString *DCTStream::getPSFilter(int psLevel, const char *indent) +std::optional DCTStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("<< >> /DCTDecode filter\n"); + s->append(indent).append("<< >> /DCTDecode filter\n"); return s; } diff --git a/poppler/DCTStream.h b/poppler/DCTStream.h index 2aaee12..6217e5e 100644 --- a/poppler/DCTStream.h +++ b/poppler/DCTStream.h @@ -11,6 +11,8 @@ // Copyright 2011 Daiki Ueno // Copyright 2013 Thomas Freitag // Copyright 2020 Lluís Batlle i Rossell +// Copyright 2025 Nelson Benítez León +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -61,10 +63,10 @@ class DCTStream : public FilterStream DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion); ~DCTStream() override; StreamKind getKind() const override { return strDCT; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: diff --git a/poppler/DateInfo.cc b/poppler/DateInfo.cc index cdba4ab..3c893e0 100644 --- a/poppler/DateInfo.cc +++ b/poppler/DateInfo.cc @@ -2,13 +2,14 @@ // // DateInfo.cc // -// Copyright (C) 2008, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2008, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2015 André Guerreiro // Copyright (C) 2015 André Esser // Copyright (C) 2016, 2018, 2021 Adrian Johnson // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden -// Copyright (C) 2021 Albert Astals Cid +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Erich E. Hoover // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -25,8 +26,8 @@ #include -#include "glibc.h" -#include "gmem.h" +#include "goo/glibc.h" +#include "goo/gmem.h" #include "DateInfo.h" #include "UTF.h" @@ -36,16 +37,14 @@ /* See PDF Reference 1.3, Section 3.8.2 for PDF Date representation */ bool parseDateString(const GooString *date, int *year, int *month, int *day, int *hour, int *minute, int *second, char *tz, int *tzHour, int *tzMinute) { - Unicode *u; - int len = TextStringToUCS4(date->toStr(), &u); + std::vector u = TextStringToUCS4(date->toStr()); GooString s; - for (int i = 0; i < len; i++) { + for (auto &c : u) { // Ignore any non ASCII characters - if (u[i] < 128) { - s.append(u[i]); + if (c < 128) { + s.append(c); } } - gfree(u); const char *dateString = s.c_str(); if (strlen(dateString) < 2) { @@ -87,16 +86,14 @@ bool parseDateString(const GooString *date, int *year, int *month, int *day, int return false; } -GooString *timeToDateString(const time_t *timeA) +std::string timeToStringWithFormat(const time_t *timeA, const char *format) { const time_t timet = timeA ? *timeA : time(nullptr); struct tm localtime_tm; localtime_r(&timet, &localtime_tm); - char buf[50]; - strftime(buf, sizeof(buf), "D:%Y%m%d%H%M%S", &localtime_tm); - GooString *dateString = new GooString(buf); + char timeOffset[12]; // strftime "%z" does not work on windows (it prints zone name, not offset) // calculate time zone offset by comparing local and gmtime time_t value for same @@ -104,14 +101,33 @@ GooString *timeToDateString(const time_t *timeA) const time_t timeg = timegm(&localtime_tm); const int offset = static_cast(difftime(timeg, timet)); // find time zone offset in seconds if (offset > 0) { - dateString->appendf("+{0:02d}'{1:02d}'", offset / 3600, (offset % 3600) / 60); + snprintf(timeOffset, sizeof(timeOffset), "+%02d'%02d'", offset / 3600, (offset % 3600) / 60); } else if (offset < 0) { - dateString->appendf("-{0:02d}'{1:02d}'", -offset / 3600, (-offset % 3600) / 60); + snprintf(timeOffset, sizeof(timeOffset), "-%02d'%02d'", -offset / 3600, (-offset % 3600) / 60); } else { - dateString->append("Z"); + snprintf(timeOffset, sizeof(timeOffset), "Z"); } + std::string fmt(format); + const char timeOffsetPattern[] = "%z"; + size_t timeOffsetPosition = fmt.find(timeOffsetPattern); + if (timeOffsetPosition != std::string::npos) { + fmt.replace(timeOffsetPosition, sizeof(timeOffsetPattern) - 1, timeOffset); + } + + if (fmt.empty()) { + return ""; + } + size_t bufLen = 50; + std::string buf(bufLen, ' '); + while (strftime(&buf[0], buf.size(), fmt.c_str(), &localtime_tm) == 0) { + buf.resize(bufLen *= 2); + } + return buf; +} - return dateString; +std::unique_ptr timeToDateString(const time_t *timeA) +{ + return std::make_unique(timeToStringWithFormat(timeA, "D:%Y%m%d%H%M%S%z")); } // Convert PDF date string to time. Returns -1 if conversion fails. diff --git a/poppler/DateInfo.h b/poppler/DateInfo.h index be349e6..f9ad22a 100644 --- a/poppler/DateInfo.h +++ b/poppler/DateInfo.h @@ -7,6 +7,8 @@ // Copyright (C) 2015 André Guerreiro // Copyright (C) 2015 André Esser // Copyright (C) 2016, 2021 Adrian Johnson +// Copyright (C) 2024 Erich E. Hoover +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -32,9 +34,15 @@ bool POPPLER_PRIVATE_EXPORT parseDateString(const GooString *date, int *year, in /* Converts the time_t into a PDF Date format string. * If timeA is NULL, current time is used. - * Returns new GooString. Free with delete. + * Returns goostring. */ -GooString POPPLER_PRIVATE_EXPORT *timeToDateString(const time_t *timeA); +std::unique_ptr POPPLER_PRIVATE_EXPORT timeToDateString(const time_t *timeA); + +/* Converts the time_t into a string with the specified format. + * If timeA is NULL, current time is used. + * Returns std::string + */ +std::string POPPLER_PRIVATE_EXPORT timeToStringWithFormat(const time_t *timeA, const char *format); /* Convert PDF date string to time. * Returns -1 if conversion fails. diff --git a/poppler/Decrypt.cc b/poppler/Decrypt.cc index 5e72951..df81bcf 100644 --- a/poppler/Decrypt.cc +++ b/poppler/Decrypt.cc @@ -22,6 +22,8 @@ // Copyright (C) 2016 Alok Anand // Copyright (C) 2016 Thomas Freitag // Copyright (C) 2018 Adam Reichold +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -347,11 +349,11 @@ BaseCryptStream::~BaseCryptStream() } } -void BaseCryptStream::reset() +bool BaseCryptStream::reset() { charactersRead = 0; nextCharBuff = EOF; - str->reset(); + return str->reset(); } Goffset BaseCryptStream::getPos() @@ -400,11 +402,11 @@ EncryptStream::EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAl } } -EncryptStream::~EncryptStream() { } +EncryptStream::~EncryptStream() = default; -void EncryptStream::reset() +bool EncryptStream::reset() { - BaseCryptStream::reset(); + bool baseResult = BaseCryptStream::reset(); switch (algo) { case cryptRC4: @@ -426,6 +428,8 @@ void EncryptStream::reset() case cryptNone: break; } + + return baseResult; } int EncryptStream::lookChar() @@ -479,12 +483,12 @@ int EncryptStream::lookChar() DecryptStream::DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref refA) : BaseCryptStream(strA, fileKey, algoA, keyLength, refA) { } -DecryptStream::~DecryptStream() { } +DecryptStream::~DecryptStream() = default; -void DecryptStream::reset() +bool DecryptStream::reset() { int i; - BaseCryptStream::reset(); + bool baseResult = BaseCryptStream::reset(); switch (algo) { case cryptRC4: @@ -508,6 +512,8 @@ void DecryptStream::reset() case cryptNone: break; } + + return baseResult; } int DecryptStream::lookChar() diff --git a/poppler/Decrypt.h b/poppler/Decrypt.h index 4f0c6d2..ebd7b0d 100644 --- a/poppler/Decrypt.h +++ b/poppler/Decrypt.h @@ -19,6 +19,8 @@ // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013, 2018, 2019, 2021 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -95,7 +97,7 @@ class BaseCryptStream : public FilterStream BaseCryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~BaseCryptStream() override; StreamKind getKind() const override { return strCrypt; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override = 0; Goffset getPos() override; @@ -127,7 +129,7 @@ class EncryptStream : public BaseCryptStream public: EncryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~EncryptStream() override; - void reset() override; + [[nodiscard]] bool reset() override; int lookChar() override; }; @@ -136,7 +138,7 @@ class DecryptStream : public BaseCryptStream public: DecryptStream(Stream *strA, const unsigned char *fileKey, CryptAlgorithm algoA, int keyLength, Ref ref); ~DecryptStream() override; - void reset() override; + [[nodiscard]] bool reset() override; int lookChar() override; }; diff --git a/poppler/Dict.cc b/poppler/Dict.cc index 789f90e..3598d78 100644 --- a/poppler/Dict.cc +++ b/poppler/Dict.cc @@ -16,7 +16,7 @@ // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2007-2008 Julien Rebetez -// Copyright (C) 2008, 2010, 2013, 2014, 2017, 2019, 2020, 2022 Albert Astals Cid +// Copyright (C) 2008, 2010, 2013, 2014, 2017, 2019, 2020, 2022, 2024 Albert Astals Cid // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag @@ -32,6 +32,7 @@ #include #include +#include #include "XRef.h" #include "Dict.h" @@ -112,19 +113,19 @@ inline const Dict::DictEntry *Dict::find(const char *key) const if (!sorted) { Dict *that = const_cast(this); - std::sort(that->entries.begin(), that->entries.end(), CmpDictEntry {}); + std::ranges::sort(that->entries, CmpDictEntry {}); that->sorted = true; } } } if (sorted) { - const auto pos = std::lower_bound(entries.begin(), entries.end(), key, CmpDictEntry {}); + const auto pos = std::ranges::lower_bound(entries, key, std::less<> {}, &DictEntry::first); if (pos != entries.end() && pos->first == key) { return &*pos; } } else { - const auto pos = std::find_if(entries.rbegin(), entries.rend(), [key](const DictEntry &entry) { return entry.first == key; }); + const auto pos = std::ranges::find_if(std::ranges::reverse_view(entries), [key](const DictEntry &entry) { return entry.first == key; }); if (pos != entries.rend()) { return &*pos; } diff --git a/poppler/Dict.h b/poppler/Dict.h index 621b76a..1b8fe64 100644 --- a/poppler/Dict.h +++ b/poppler/Dict.h @@ -16,7 +16,7 @@ // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2007-2008 Julien Rebetez -// Copyright (C) 2010, 2017-2022 Albert Astals Cid +// Copyright (C) 2010, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson @@ -128,4 +128,73 @@ class POPPLER_PRIVATE_EXPORT Dict DictEntry *find(const char *key); }; +//------------------------------------------------------------------------ +// Object Dict accessors. +//------------------------------------------------------------------------ + +inline int Object::dictGetLength() const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->getLength(); +} + +inline void Object::dictAdd(const char *key, Object &&val) +{ + OBJECT_TYPE_CHECK(objDict); + dict->add(key, std::move(val)); +} + +inline void Object::dictSet(const char *key, Object &&val) +{ + OBJECT_TYPE_CHECK(objDict); + dict->set(key, std::move(val)); +} + +inline void Object::dictRemove(const char *key) +{ + OBJECT_TYPE_CHECK(objDict); + dict->remove(key); +} + +inline bool Object::dictIs(const char *dictType) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->is(dictType); +} + +inline bool Object::isDict(const char *dictType) const +{ + return type == objDict && dictIs(dictType); +} + +inline Object Object::dictLookup(const char *key, int recursion) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->lookup(key, recursion); +} + +inline const Object &Object::dictLookupNF(const char *key) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->lookupNF(key); +} + +inline const char *Object::dictGetKey(int i) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->getKey(i); +} + +inline Object Object::dictGetVal(int i) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->getVal(i); +} + +inline const Object &Object::dictGetValNF(int i) const +{ + OBJECT_TYPE_CHECK(objDict); + return dict->getValNF(i); +} + #endif diff --git a/poppler/DistinguishedNameParser.h b/poppler/DistinguishedNameParser.h new file mode 100644 index 0000000..e01dbd7 --- /dev/null +++ b/poppler/DistinguishedNameParser.h @@ -0,0 +1,322 @@ +//======================================================================== +// +// DistinguishedNameParser.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2002 g10 Code GmbH +// Copyright 2004 Klarälvdalens Datakonsult AB +// Copyright 2021 g10 Code GmbH +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2024 Albert Astals Cid +// +// Derived from libkleopatra (KDE key management library) dn.cpp +// +//======================================================================== + +#ifndef DISTINGUISHEDNAMEPARSER_H +#define DISTINGUISHEDNAMEPARSER_H + +#include +#include +#include +#include +#include + +namespace DN { +namespace detail { + +inline std::string_view removeLeadingSpaces(std::string_view view) +{ + auto pos = view.find_first_not_of(' '); + if (pos > view.size()) { + return {}; + } + return view.substr(pos); +} + +inline std::string_view removeTrailingSpaces(std::string_view view) +{ + auto pos = view.find_last_not_of(' '); + if (pos > view.size()) { + return {}; + } + return view.substr(0, pos + 1); +} + +inline unsigned char xtoi(unsigned char c) +{ + if (c <= '9') { + return c - '0'; + } + if (c <= 'F') { + return c - 'A' + 10; + } + return c < 'a' + 10; +} + +inline unsigned char xtoi(unsigned char first, unsigned char second) +{ + return 16 * xtoi(first) + xtoi(second); +} +// Parses a hex string into actual content +inline std::optional parseHexString(std::string_view view) +{ + auto size = view.size(); + if (size == 0 || (size % 2 == 1)) { + return std::nullopt; + } + // It is only supposed to be called with actual hex strings + // but this is just to be extra sure + auto endHex = view.find_first_not_of("1234567890abcdefABCDEF"); + if (endHex != std::string_view::npos) { + return {}; + } + std::string result; + result.reserve(size / 2); + for (size_t i = 0; i < (view.size() - 1); i += 2) { + result.push_back(xtoi(view[i], view[i + 1])); + } + return result; +} + +static const std::vector> oidmap = { + // clang-format off + // keep them ordered by oid: + {"NameDistinguisher", "0.2.262.1.10.7.20" }, + {"EMAIL", "1.2.840.113549.1.9.1"}, + {"CN", "2.5.4.3" }, + {"SN", "2.5.4.4" }, + {"SerialNumber", "2.5.4.5" }, + {"T", "2.5.4.12" }, + {"D", "2.5.4.13" }, + {"BC", "2.5.4.15" }, + {"ADDR", "2.5.4.16" }, + {"PC", "2.5.4.17" }, + {"GN", "2.5.4.42" }, + {"Pseudo", "2.5.4.65" }, + // clang-format on +}; + +static std::string_view attributeNameForOID(std::string_view oid) +{ + if (oid.substr(0, 4) == std::string_view { "OID." } || oid.substr(0, 4) == std::string_view { "oid." }) { // c++20 has starts_with. we don't have that yet. + oid.remove_prefix(4); + } + for (const auto &m : oidmap) { + if (oid == m.second) { + return m.first; + } + } + return {}; +} + +/* Parse a DN and return an array-ized one. This is not a validating + parser and it does not support any old-stylish syntax; gpgme is + expected to return only rfc2253 compatible strings. */ +static std::pair, std::pair> parse_dn_part(std::string_view stringv) +{ + std::pair dnPair; + auto separatorPos = stringv.find_first_of('='); + if (separatorPos == 0 || separatorPos == std::string_view::npos) { + return {}; /* empty key */ + } + + std::string_view key = stringv.substr(0, separatorPos); + key = removeTrailingSpaces(key); + // map OIDs to their names: + if (auto name = attributeNameForOID(key); !name.empty()) { + key = name; + } + + dnPair.first = std::string { key }; + stringv = removeLeadingSpaces(stringv.substr(separatorPos + 1)); + if (stringv.empty()) { + return {}; + } + + if (stringv.front() == '#') { + /* hexstring */ + stringv.remove_prefix(1); + auto endHex = stringv.find_first_not_of("1234567890abcdefABCDEF"); + if (!endHex || (endHex % 2 == 1)) { + return {}; /* empty or odd number of digits */ + } + auto value = parseHexString(stringv.substr(0, endHex)); + if (!value.has_value()) { + return {}; + } + stringv = stringv.substr(endHex); + dnPair.second = value.value(); + } else if (stringv.front() == '"') { + stringv.remove_prefix(1); + std::string value; + bool stop = false; + while (!stringv.empty() && !stop) { + switch (stringv.front()) { + case '\\': { + if (stringv.size() < 2) { + return {}; + } + if (stringv[1] == '"') { + value.push_back('"'); + stringv.remove_prefix(2); + } else { + // it is a bit unclear in rfc2253 if escaped hex chars should + // be decoded inside quotes. Let's just forward the verbatim + // for now + value.push_back(stringv.front()); + value.push_back(stringv[1]); + stringv.remove_prefix(2); + } + break; + } + case '"': { + stop = true; + stringv.remove_prefix(1); + break; + } + default: { + value.push_back(stringv.front()); + stringv.remove_prefix(1); + } + } + } + if (!stop) { + // we have reached end of string, but never an actual ", so error out + return {}; + } + dnPair.second = value; + } else { + std::string value; + bool stop = false; + bool lastAddedEscapedSpace = false; + while (!stringv.empty() && !stop) { + switch (stringv.front()) { + case '\\': //_escaping + { + stringv.remove_prefix(1); + if (stringv.empty()) { + return {}; + } + switch (stringv.front()) { + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': + case '\\': + case '"': + case ' ': { + if (stringv.front() == ' ') { + lastAddedEscapedSpace = true; + } else { + lastAddedEscapedSpace = false; + } + value.push_back(stringv.front()); + stringv.remove_prefix(1); + break; + } + default: { + if (stringv.size() < 2) { + // this should be double hex-ish, but isn't. + return {}; + } + if (std::isxdigit(stringv.front()) && std::isxdigit(stringv[1])) { + lastAddedEscapedSpace = false; + value.push_back(xtoi(stringv.front(), stringv[1])); + stringv.remove_prefix(2); + break; + } else { + // invalid escape + return {}; + } + } + } + break; + } + case '"': + // unescaped " in the middle; not allowed + return {}; + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': { + stop = true; + break; // + } + default: + lastAddedEscapedSpace = false; + value.push_back(stringv.front()); + stringv.remove_prefix(1); + } + } + if (lastAddedEscapedSpace) { + dnPair.second = value; + } else { + dnPair.second = std::string { removeTrailingSpaces(value) }; + } + } + return { stringv, dnPair }; +} +} + +using Result = std::vector>; + +/* Parse a DN and return an array-ized one. This is not a validating + parser and it does not support any old-stylish syntax; gpgme is + expected to return only rfc2253 compatible strings. */ +static Result parseString(std::string_view string) +{ + Result result; + while (!string.empty()) { + string = detail::removeLeadingSpaces(string); + if (string.empty()) { + break; + } + + auto [partResult, dnPair] = detail::parse_dn_part(string); + if (!partResult.has_value()) { + return {}; + } + + string = partResult.value(); + if (!dnPair.first.empty() && !dnPair.second.empty()) { + result.emplace_back(std::move(dnPair)); + } + + string = detail::removeLeadingSpaces(string); + if (string.empty()) { + break; + } + switch (string.front()) { + case ',': + case ';': + case '+': + string.remove_prefix(1); + break; + default: + // some unexpected characters here + return {}; + } + } + return result; +} + +/// returns the first value of a given key (note. there can be multiple) +/// or nullopt if key is not available +inline std::optional FindFirstValue(const Result &dn, std::string_view key) +{ + auto first = std::ranges::find_if(dn, [&key](const auto &it) { return it.first == key; }); + if (first == dn.end()) { + return {}; + } + return first->second; +} +} // namespace DN +#endif // DISTINGUISHEDNAMEPARSER_H diff --git a/poppler/Error.cc b/poppler/Error.cc index f821bba..45af873 100644 --- a/poppler/Error.cc +++ b/poppler/Error.cc @@ -19,6 +19,9 @@ // Copyright (C) 2012 Marek Kasik // Copyright (C) 2013, 2017 Adrian Johnson // Copyright (C) 2020 Adam Reichold +// Copyright (C) 2024 Oliver Sander +// Copyright (C) 2024 Vincent Lefevre +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -31,7 +34,7 @@ #include #include #include -#include "GooString.h" +#include "goo/GooString.h" #include "GlobalParams.h" #include "Error.h" @@ -53,12 +56,11 @@ void CDECL error(ErrorCategory category, Goffset pos, const char *msg, ...) return; } va_start(args, msg); - const std::unique_ptr s = GooString::formatv(msg, args); + const std::string s = GooString::formatv(msg, args); va_end(args); GooString sanitized; - for (int i = 0; i < s->getLength(); ++i) { - const char c = s->getChar(i); + for (const char c : s) { if (c < (char)0x20 || c >= (char)0x7f) { sanitized.appendf("<{0:02x}>", c & 0xff); } else { diff --git a/poppler/FDPDFDocBuilder.cc b/poppler/FDPDFDocBuilder.cc index dcfbbac..1382b29 100644 --- a/poppler/FDPDFDocBuilder.cc +++ b/poppler/FDPDFDocBuilder.cc @@ -8,6 +8,7 @@ // Copyright 2010, 2017, 2021, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // Copyright 2021 Christian Persch +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -34,7 +35,7 @@ int FileDescriptorPDFDocBuilder::parseFdFromUri(const GooString &uri) return fd; } -std::unique_ptr FileDescriptorPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) +std::unique_ptr FileDescriptorPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword) { const auto fd = parseFdFromUri(uri); if (fd == -1) { diff --git a/poppler/FDPDFDocBuilder.h b/poppler/FDPDFDocBuilder.h index 42c0ff6..017d0d8 100644 --- a/poppler/FDPDFDocBuilder.h +++ b/poppler/FDPDFDocBuilder.h @@ -8,6 +8,7 @@ // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander // Copyright 2021 Christian Persch +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -26,7 +27,7 @@ class FileDescriptorPDFDocBuilder : public PDFDocBuilder { public: - std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; + std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}) override; bool supports(const GooString &uri) override; private: diff --git a/poppler/FileSpec.cc b/poppler/FileSpec.cc index a9e2f67..d067ffc 100644 --- a/poppler/FileSpec.cc +++ b/poppler/FileSpec.cc @@ -7,11 +7,12 @@ // // Copyright (C) 2008-2009 Carlos Garcia Campos // Copyright (C) 2009 Kovid Goyal -// Copyright (C) 2012, 2017-2021 Albert Astals Cid +// Copyright (C) 2012, 2017-2021, 2024 Albert Astals Cid // Copyright (C) 2012 Hib Eris // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Christian Persch +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -49,7 +50,7 @@ EmbFile::EmbFile(Object &&efStream) // subtype is normally the mimetype Object subtypeName = dataDict->lookup("Subtype"); if (subtypeName.isName()) { - m_mimetype = new GooString(subtypeName.getName()); + m_mimetype = std::make_unique(subtypeName.getName()); } // paramDict corresponds to Table 3.42 in the PDF1.6 spec @@ -57,12 +58,12 @@ EmbFile::EmbFile(Object &&efStream) if (paramDict.isDict()) { Object paramObj = paramDict.dictLookup("ModDate"); if (paramObj.isString()) { - m_modDate = new GooString(paramObj.getString()); + m_modDate = paramObj.getString()->copy(); } paramObj = paramDict.dictLookup("CreationDate"); if (paramObj.isString()) { - m_createDate = new GooString(paramObj.getString()); + m_createDate = paramObj.getString()->copy(); } paramObj = paramDict.dictLookup("Size"); @@ -72,26 +73,20 @@ EmbFile::EmbFile(Object &&efStream) paramObj = paramDict.dictLookup("CheckSum"); if (paramObj.isString()) { - m_checksum = new GooString(paramObj.getString()); + m_checksum = paramObj.getString()->copy(); } } } } -EmbFile::~EmbFile() -{ - delete m_createDate; - delete m_modDate; - delete m_checksum; - delete m_mimetype; -} +EmbFile::~EmbFile() = default; -bool EmbFile::save(const char *path) +bool EmbFile::save(const std::string &path) { FILE *f; bool ret; - if (!(f = openFile(path, "wb"))) { + if (!(f = openFile(path.c_str(), "wb"))) { return false; } ret = save2(f); @@ -107,7 +102,9 @@ bool EmbFile::save2(FILE *f) return false; } - m_objStr.streamReset(); + if (!m_objStr.streamReset()) { + return false; + } while ((c = m_objStr.streamGetChar()) != EOF) { fputc(c, f); } @@ -117,10 +114,7 @@ bool EmbFile::save2(FILE *f) FileSpec::FileSpec(const Object *fileSpecA) { ok = true; - fileName = nullptr; - platformFileName = nullptr; embFile = nullptr; - desc = nullptr; fileSpec = fileSpecA->copy(); Object obj1 = getFileSpecName(fileSpecA); @@ -151,13 +145,7 @@ FileSpec::FileSpec(const Object *fileSpecA) } } -FileSpec::~FileSpec() -{ - delete fileName; - delete platformFileName; - delete embFile; - delete desc; -} +FileSpec::~FileSpec() = default; EmbFile *FileSpec::getEmbeddedFile() { @@ -166,13 +154,13 @@ EmbFile *FileSpec::getEmbeddedFile() } if (embFile) { - return embFile; + return embFile.get(); } XRef *xref = fileSpec.getDict()->getXRef(); - embFile = new EmbFile(fileStream.fetch(xref)); + embFile = std::make_unique(fileStream.fetch(xref)); - return embFile; + return embFile.get(); } Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName) @@ -195,7 +183,7 @@ Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string Dict *fsDict = new Dict(xref); fsDict->set("Type", Object(objName, "Filespec")); - fsDict->set("UF", Object(new GooString(fileName))); + fsDict->set("UF", Object(std::make_unique(fileName))); fsDict->set("EF", Object(efDict)); return Object(fsDict); @@ -204,7 +192,7 @@ Object FileSpec::newFileSpecObject(XRef *xref, GooFile *file, const std::string GooString *FileSpec::getFileNameForPlatform() { if (platformFileName) { - return platformFileName; + return platformFileName.get(); } Object obj1 = getFileSpecNameForPlatform(&fileSpec); @@ -212,7 +200,7 @@ GooString *FileSpec::getFileNameForPlatform() platformFileName = obj1.getString()->copy(); } - return platformFileName; + return platformFileName.get(); } Object getFileSpecName(const Object *fileSpec) @@ -278,7 +266,7 @@ Object getFileSpecNameForPlatform(const Object *fileSpec) // system-dependent path manipulation #ifdef _WIN32 int i, j; - GooString *name = fileName.getString()->copy(); + std::unique_ptr name = fileName.getString()->copy(); // "//...." --> "\...." // "/x/...." --> "x:\...." // "/server/share/...." --> "\\server\share\...." @@ -312,7 +300,7 @@ Object getFileSpecNameForPlatform(const Object *fileSpec) name->del(i); } } - fileName = Object(name); + fileName = Object(std::move(name)); #endif /* _WIN32 */ return fileName; diff --git a/poppler/FileSpec.h b/poppler/FileSpec.h index 09e05ca..d1d5bd3 100644 --- a/poppler/FileSpec.h +++ b/poppler/FileSpec.h @@ -6,7 +6,8 @@ // under GPL version 2 or later // // Copyright (C) 2008 Carlos Garcia Campos -// Copyright (C) 2017-2019, 2021 Albert Astals Cid +// Copyright (C) 2017-2019, 2021, 2024 Albert Astals Cid +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -29,23 +30,23 @@ class POPPLER_PRIVATE_EXPORT EmbFile EmbFile &operator=(const EmbFile &) = delete; int size() const { return m_size; } - const GooString *modDate() const { return m_modDate; } - const GooString *createDate() const { return m_createDate; } - const GooString *checksum() const { return m_checksum; } - const GooString *mimeType() const { return m_mimetype; } + const GooString *modDate() const { return m_modDate.get(); } + const GooString *createDate() const { return m_createDate.get(); } + const GooString *checksum() const { return m_checksum.get(); } + const GooString *mimeType() const { return m_mimetype.get(); } Object *streamObject() { return &m_objStr; } Stream *stream() { return isOk() ? m_objStr.getStream() : nullptr; } bool isOk() const { return m_objStr.isStream(); } - bool save(const char *path); + bool save(const std::string &path); private: bool save2(FILE *f); int m_size; - GooString *m_createDate; - GooString *m_modDate; - GooString *m_checksum; - GooString *m_mimetype; + std::unique_ptr m_createDate; + std::unique_ptr m_modDate; + std::unique_ptr m_checksum; + std::unique_ptr m_mimetype; Object m_objStr; }; @@ -60,9 +61,9 @@ class POPPLER_PRIVATE_EXPORT FileSpec bool isOk() const { return ok; } - const GooString *getFileName() const { return fileName; } + const GooString *getFileName() const { return fileName.get(); } GooString *getFileNameForPlatform(); - const GooString *getDescription() const { return desc; } + const GooString *getDescription() const { return desc.get(); } EmbFile *getEmbeddedFile(); static Object newFileSpecObject(XRef *xref, GooFile *file, const std::string &fileName); @@ -72,11 +73,11 @@ class POPPLER_PRIVATE_EXPORT FileSpec Object fileSpec; - GooString *fileName; // F, UF, DOS, Mac, Unix - GooString *platformFileName; + std::unique_ptr fileName; // F, UF, DOS, Mac, Unix + std::unique_ptr platformFileName; Object fileStream; // Ref to F entry in UF - EmbFile *embFile; - GooString *desc; // Desc + std::unique_ptr embFile; + std::unique_ptr desc; // Desc }; Object getFileSpecName(const Object *fileSpec); diff --git a/poppler/FlateEncoder.cc b/poppler/FlateEncoder.cc index 0786565..1cfbd64 100644 --- a/poppler/FlateEncoder.cc +++ b/poppler/FlateEncoder.cc @@ -6,6 +6,8 @@ // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2021 Even Rouault // Copyright (C) 2022 Albert Astals Cid +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // This file is under the GPLv2 or later license // @@ -31,7 +33,7 @@ FlateEncoder::FlateEncoder(Stream *strA) : FilterStream(strA) // -Wzero-as-null-pointer-constant warning. // For safety, check that the Z_NULL definition is equivalent to // 0 / null pointer. - static_assert(Z_NULL == 0); + static_assert(static_cast(Z_NULL) == 0); zlib_stream.zalloc = nullptr; zlib_stream.zfree = nullptr; zlib_stream.opaque = nullptr; @@ -55,11 +57,11 @@ FlateEncoder::~FlateEncoder() } } -void FlateEncoder::reset() +bool FlateEncoder::reset() { int zlib_status; - str->reset(); + bool innerReset = str->reset(); outBufPtr = outBufEnd = outBuf; inBufEof = outBufEof = false; @@ -71,10 +73,13 @@ void FlateEncoder::reset() if (zlib_status != Z_OK) { inBufEof = outBufEof = true; error(errInternal, -1, "Internal: deflateInit() failed in FlateEncoder::reset()"); + return false; } zlib_stream.next_out = outBufEnd; zlib_stream.avail_out = 1; /* anything but 0 to trigger a read */ + + return innerReset; } bool FlateEncoder::fillBuf() diff --git a/poppler/FlateEncoder.h b/poppler/FlateEncoder.h index e42dc4e..f667478 100644 --- a/poppler/FlateEncoder.h +++ b/poppler/FlateEncoder.h @@ -4,6 +4,8 @@ // // Copyright (C) 2016, William Bader // Copyright (C) 2018, 2019, 2021 Albert Astals Cid +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // This file is under the GPLv2 or later license // @@ -43,10 +45,10 @@ class FlateEncoder : public FilterStream explicit FlateEncoder(Stream *strA); ~FlateEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (outBufPtr >= outBufEnd && !fillBuf()) ? EOF : (*outBufPtr++ & 0xff); } int lookChar() override { return (outBufPtr >= outBufEnd && !fillBuf()) ? EOF : (*outBufPtr & 0xff); } - GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } + std::optional getPSFilter(int psLevel, const char *indent) override { return {}; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } diff --git a/poppler/FlateStream.cc b/poppler/FlateStream.cc index a97b687..2e23a31 100644 --- a/poppler/FlateStream.cc +++ b/poppler/FlateStream.cc @@ -6,6 +6,7 @@ // Copyright (C) 2010, 2021, Albert Astals Cid // Copyright (C) 2016, William Bader // Copyright (C) 2017, Adrian Johnson +// Copyright (C) 2025 Nelson Benítez León // // This file is under the GPLv2 or later license // @@ -42,7 +43,7 @@ FlateStream::~FlateStream() delete str; } -void FlateStream::reset() +bool FlateStream::reset() { // FIXME: what are the semantics of reset? // i.e. how much initialization has to happen in the constructor? @@ -57,6 +58,8 @@ void FlateStream::reset() status = Z_OK; out_pos = 0; out_buf_len = 0; + + return true; } int FlateStream::getRawChar() diff --git a/poppler/FlateStream.h b/poppler/FlateStream.h index 88f69be..12dd6ac 100644 --- a/poppler/FlateStream.h +++ b/poppler/FlateStream.h @@ -4,6 +4,8 @@ // // Copyright (C) 2005, Jeff Muizelaar // Copyright (C) 2010, 2011, 2019, 2021, Albert Astals Cid +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // This file is under the GPLv2 or later license // @@ -38,7 +40,7 @@ class FlateStream : public FilterStream FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); virtual ~FlateStream(); StreamKind getKind() const override { return strFlate; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; int getRawChar() override; diff --git a/poppler/FontInfo.cc b/poppler/FontInfo.cc index 309ec6d..ef458ad 100644 --- a/poppler/FontInfo.cc +++ b/poppler/FontInfo.cc @@ -3,7 +3,7 @@ // FontInfo.cc // // Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005-2008, 2010, 2017-2020 Albert Astals Cid +// Copyright (C) 2005-2008, 2010, 2017-2020, 2023-2025 Albert Astals Cid // Copyright (C) 2005 Brad Hards // Copyright (C) 2006 Kouhei Sutou // Copyright (C) 2009 Pino Toscano @@ -15,6 +15,7 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2019, 2021, 2022 Oliver Sander +// Copyright (C) 2023 Suzuki Toshiya // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -50,7 +51,7 @@ FontInfoScanner::FontInfoScanner(PDFDoc *docA, int firstPage) currentPage = firstPage + 1; } -FontInfoScanner::~FontInfoScanner() { } +FontInfoScanner::~FontInfoScanner() = default; std::vector FontInfoScanner::scan(int nPages) { @@ -97,23 +98,13 @@ std::vector FontInfoScanner::scan(int nPages) void FontInfoScanner::scanFonts(XRef *xrefA, Dict *resDict, std::vector *fontsList) { - GfxFontDict *gfxFontDict; - // scan the fonts in this resource dictionary - gfxFontDict = nullptr; - const Object &fontObj = resDict->lookupNF("Font"); - if (fontObj.isRef()) { - Object obj2 = fontObj.fetch(xrefA); - if (obj2.isDict()) { - Ref r = fontObj.getRef(); - gfxFontDict = new GfxFontDict(xrefA, &r, obj2.getDict()); - } - } else if (fontObj.isDict()) { - gfxFontDict = new GfxFontDict(xrefA, nullptr, fontObj.getDict()); - } - if (gfxFontDict) { - for (int i = 0; i < gfxFontDict->getNumFonts(); ++i) { - if (const std::shared_ptr &font = gfxFontDict->getFont(i)) { + Ref fontDictRef; + const Object &fontObj = resDict->lookup("Font", &fontDictRef); + if (fontObj.isDict()) { + GfxFontDict gfxFontDict(xrefA, fontDictRef, fontObj.getDict()); + for (int i = 0; i < gfxFontDict.getNumFonts(); ++i) { + if (const std::shared_ptr &font = gfxFontDict.getFont(i)) { Ref fontRef = *font->getID(); // add this font to the list if not already found @@ -122,33 +113,31 @@ void FontInfoScanner::scanFonts(XRef *xrefA, Dict *resDict, std::vectorlookup(resType); + Ref objDictRef; + Object objDict = resDict->lookup(resType, &objDictRef); + if (!visitedObjects.insert(objDictRef)) { + continue; + } if (objDict.isDict()) { for (int i = 0; i < objDict.dictGetLength(); ++i) { Ref obj2Ref; const Object obj2 = objDict.getDict()->getVal(i, &obj2Ref); - if (obj2Ref != Ref::INVALID()) { - // check for an already-seen object - if (!visitedObjects.insert(obj2Ref.num).second) { - continue; - } + // check for an already-seen object + if (!visitedObjects.insert(obj2Ref)) { + continue; } if (obj2.isStream()) { Ref resourcesRef; const Object resObj = obj2.streamGetDict()->lookup("Resources", &resourcesRef); - - if (resourcesRef != Ref::INVALID()) { - if (!visitedObjects.insert(resourcesRef.num).second) { - continue; - } + if (!visitedObjects.insert(resourcesRef)) { + continue; } if (resObj.isDict() && resObj.getDict() != resDict) { @@ -167,7 +156,7 @@ FontInfo::FontInfo(GfxFont *font, XRef *xref) // font name const std::optional &origName = font->getName(); if (origName) { - name = *font->getName(); + name = *origName; } // font type @@ -176,17 +165,16 @@ FontInfo::FontInfo(GfxFont *font, XRef *xref) // check for an embedded font if (font->getType() == fontType3) { emb = true; + embRef = Ref::INVALID(); } else { emb = font->getEmbeddedFontID(&embRef); } if (!emb) { - SysFontType dummy; - int dummy2; GooString substituteNameAux; - std::unique_ptr tmpFile(globalParams->findSystemFontFile(font, &dummy, &dummy2, &substituteNameAux)); - if (tmpFile) { - file = tmpFile->toStr(); + const std::optional fontLoc = font->locateFont(xref, nullptr, &substituteNameAux); + if (fontLoc && fontLoc->locType == gfxFontLocExternal) { + file = fontLoc->path; } if (substituteNameAux.getLength() > 0) { substituteName = substituteNameAux.toStr(); diff --git a/poppler/FontInfo.h b/poppler/FontInfo.h index d13ae97..ba37a74 100644 --- a/poppler/FontInfo.h +++ b/poppler/FontInfo.h @@ -3,7 +3,7 @@ // FontInfo.h // // Copyright (C) 2005 Kristian Høgsberg -// Copyright (C) 2005-2008, 2010, 2011, 2018, 2019, 2021 Albert Astals Cid +// Copyright (C) 2005-2008, 2010, 2011, 2018, 2019, 2021, 2023 Albert Astals Cid // Copyright (C) 2005 Brad Hards // Copyright (C) 2009 Pino Toscano // Copyright (C) 2012 Adrian Johnson @@ -101,7 +101,7 @@ class POPPLER_PRIVATE_EXPORT FontInfoScanner PDFDoc *doc; int currentPage; std::unordered_set fonts; - std::unordered_set visitedObjects; + RefRecursionChecker visitedObjects; void scanFonts(XRef *xrefA, Dict *resDict, std::vector *fontsList); }; diff --git a/poppler/Form.cc b/poppler/Form.cc index 5e885fa..da20b82 100644 --- a/poppler/Form.cc +++ b/poppler/Form.cc @@ -5,7 +5,7 @@ // This file is licensed under the GPLv2 or later // // Copyright 2006-2008 Julien Rebetez -// Copyright 2007-2012, 2015-2022 Albert Astals Cid +// Copyright 2007-2012, 2015-2025 Albert Astals Cid // Copyright 2007-2008, 2011 Carlos Garcia Campos // Copyright 2007, 2013, 2016, 2019, 2022 Adrian Johnson // Copyright 2007 Iñigo Martínez @@ -24,22 +24,27 @@ // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2018 Adam Reichold // Copyright 2018-2022 Nelson Benítez León -// Copyright 2019, 2020 Oliver Sander +// Copyright 2019, 2020 2024, Oliver Sander // Copyright 2019 Tomoyuki Kubota // Copyright 2019 João Netto // Copyright 2020-2022 Marek Kasik // Copyright 2020 Thorsten Behrens -// Copyright 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden +// Copyright 2020, 2023, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 Theofilos Intzoglou // Copyright 2021 Even Rouault // Copyright 2022 Alexander Sulfrian -// Copyright 2022 Erich E. Hoover +// Copyright 2022, 2024 Erich E. Hoover +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2024 Pratham Gandhi +// Copyright (C) 2024 Vincent Lefevre +// Copyright 2025 Juraj Šarinay // //======================================================================== #include +#include #include #include #include @@ -47,7 +52,6 @@ #include #include #include "goo/ft_utils.h" -#include "goo/gmem.h" #include "goo/gfile.h" #include "goo/GooString.h" #include "Error.h" @@ -62,9 +66,7 @@ #include "Form.h" #include "PDFDoc.h" #include "DateInfo.h" -#ifdef ENABLE_NSS3 -# include "SignatureHandler.h" -#endif +#include "CryptoSignBackend.h" #include "SignatureInfo.h" #include "CertificateInfo.h" #include "XRef.h" @@ -73,40 +75,45 @@ #include "Link.h" #include "Lexer.h" #include "Parser.h" +#include "CIDFontsWidthsBuilder.h" +#include "UTF.h" #include "fofi/FoFiTrueType.h" #include "fofi/FoFiIdentifier.h" #include +#include #include FT_FREETYPE_H +#include + +// helper for using std::visit to get a dependent false for static_asserts +// to help get compile errors if one ever extends variants +template +inline constexpr bool always_false_v = false; // return a newly allocated char* containing an UTF16BE string of size length -char *pdfDocEncodingToUTF16(const std::string &orig, int *length) +std::string pdfDocEncodingToUTF16(const std::string &orig) { // double size, a unicode char takes 2 char, add 2 for the unicode marker - *length = 2 + 2 * orig.size(); - char *result = new char[(*length)]; - const char *cstring = orig.c_str(); + int length = 2 + 2 * orig.size(); + std::string result; + result.reserve(length); // unicode marker - result[0] = '\xfe'; - result[1] = '\xff'; + result.push_back('\xfe'); + result.push_back('\xff'); // convert to utf16 - for (int i = 2, j = 0; i < (*length); i += 2, j++) { - Unicode u = pdfDocEncoding[(unsigned int)((unsigned char)cstring[j])] & 0xffff; - result[i] = (u >> 8) & 0xff; - result[i + 1] = u & 0xff; + for (int i = 2, j = 0; i < (length); i += 2, j++) { + Unicode u = pdfDocEncoding[(unsigned int)((unsigned char)orig[j])] & 0xffff; + result.push_back((u >> 8) & 0xff); + result.push_back(u & 0xff); } return result; } -static GooString *convertToUtf16(GooString *pdfDocEncodingString) +static std::unique_ptr convertToUtf16(GooString *pdfDocEncodingString) { - int tmp_length; - char *tmp_str = pdfDocEncodingToUTF16(pdfDocEncodingString->toStr(), &tmp_length); - delete pdfDocEncodingString; - pdfDocEncodingString = new GooString(tmp_str + 2, tmp_length - 2); // Remove the unicode BOM - delete[] tmp_str; - return pdfDocEncodingString; + std::string tmpStr = pdfDocEncodingToUTF16(pdfDocEncodingString->toStr()); + return std::make_unique(tmpStr.c_str() + 2, tmpStr.size() - 2); // Remove the unicode BOM } FormWidget::FormWidget(PDFDoc *docA, Object *aobj, unsigned num, Ref aref, FormField *fieldA) @@ -197,7 +204,7 @@ const GooString *FormWidget::getMappingName() const return field->getMappingName(); } -GooString *FormWidget::getFullyQualifiedName() +const GooString *FormWidget::getFullyQualifiedName() { return field->getFullyQualifiedName(); } @@ -212,7 +219,7 @@ std::unique_ptr FormWidget::getAdditionalAction(Annot::FormAdditiona return widget ? widget->getFormAdditionalAction(t) : nullptr; } -bool FormWidget::setAdditionalAction(Annot::FormAdditionalActionsType t, const GooString &js) +bool FormWidget::setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js) { if (!widget) { return false; @@ -224,7 +231,6 @@ bool FormWidget::setAdditionalAction(Annot::FormAdditionalActionsType t, const G FormWidgetButton::FormWidgetButton(PDFDoc *docA, Object *dictObj, unsigned num, Ref refA, FormField *p) : FormWidget(docA, dictObj, num, refA, p) { type = formButton; - onStr = nullptr; // Find the name of the ON state in the AP dictionary // The reference say the Off state, if it exists, _must_ be stored in the AP dict under the name /Off @@ -236,7 +242,7 @@ FormWidgetButton::FormWidgetButton(PDFDoc *docA, Object *dictObj, unsigned num, for (int i = 0; i < obj2.dictGetLength(); i++) { const char *key = obj2.dictGetKey(i); if (strcmp(key, "Off") != 0) { - onStr = new GooString(key); + onStr = std::make_unique(key); break; } } @@ -255,10 +261,7 @@ const char *FormWidgetButton::getOnStr() const return parent()->getButtonType() == formButtonCheck ? "Yes" : nullptr; } -FormWidgetButton::~FormWidgetButton() -{ - delete onStr; -} +FormWidgetButton::~FormWidgetButton() = default; FormButtonType FormWidgetButton::getButtonType() const { @@ -336,7 +339,7 @@ void FormWidgetButton::setState(bool astate) if (found_related) { FormFieldButton *ffb = static_cast(wid->getField()); if (ffb == nullptr) { - error(errInternal, -1, "FormWidgetButton::setState : FormFieldButton expected\n"); + error(errInternal, -1, "FormWidgetButton::setState : FormFieldButton expected"); continue; } ffb->setState((char *)"Off", true); @@ -421,14 +424,14 @@ void FormWidgetText::setTextFontSize(int fontSize) parent()->setTextFontSize(fontSize); } -void FormWidgetText::setContent(const GooString *new_content) +void FormWidgetText::setContent(std::unique_ptr new_content) { - parent()->setContentCopy(new_content); + parent()->setContent(std::move(new_content)); } -void FormWidgetText::setAppearanceContent(const GooString *new_content) +void FormWidgetText::setAppearanceContent(std::unique_ptr new_content) { - parent()->setAppearanceContentCopy(new_content); + parent()->setAppearanceContent(std::move(new_content)); } FormFieldText *FormWidgetText::parent() const @@ -441,7 +444,7 @@ FormWidgetChoice::FormWidgetChoice(PDFDoc *docA, Object *dictObj, unsigned num, type = formChoice; } -FormWidgetChoice::~FormWidgetChoice() { } +FormWidgetChoice::~FormWidgetChoice() = default; bool FormWidgetChoice::_checkRange(int i) const { @@ -476,7 +479,7 @@ void FormWidgetChoice::deselectAll() const GooString *FormWidgetChoice::getEditChoice() const { if (!hasEdit()) { - error(errInternal, -1, "FormFieldChoice::getEditChoice called on a non-editable choice\n"); + error(errInternal, -1, "FormFieldChoice::getEditChoice called on a non-editable choice"); return nullptr; } return parent()->getEditChoice(); @@ -497,14 +500,19 @@ bool FormWidgetChoice::isSelected(int i) const return parent()->isSelected(i); } -void FormWidgetChoice::setEditChoice(const GooString *new_content) +void FormWidgetChoice::setEditChoice(std::unique_ptr new_content) { if (!hasEdit()) { - error(errInternal, -1, "FormFieldChoice::setEditChoice : trying to edit an non-editable choice\n"); + error(errInternal, -1, "FormFieldChoice::setEditChoice : trying to edit an non-editable choice"); return; } - parent()->setEditChoice(new_content); + parent()->setEditChoice(std::move(new_content)); +} + +void FormWidgetChoice::setAppearanceChoiceContent(std::unique_ptr new_content) +{ + parent()->setAppearanceChoiceContent(std::move(new_content)); } int FormWidgetChoice::getNumChoices() const @@ -562,20 +570,27 @@ FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned type = formSignature; } -const GooString *FormWidgetSignature::getSignature() const +const std::vector &FormWidgetSignature::getSignature() const { return static_cast(field)->getSignature(); } -SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA) +SignatureInfo *FormWidgetSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback) { - return static_cast(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA); + return static_cast(field)->validateSignatureAsync(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA, doneCallback); +} + +CertificateValidationStatus FormWidgetSignature::validateSignatureResult() +{ + return static_cast(field)->validateSignatureResult(); } -#ifdef ENABLE_NSS3 // update hash with the specified range of data from the file -static bool hashFileRange(FILE *f, SignatureHandler *handler, Goffset start, Goffset end) +static bool hashFileRange(FILE *f, CryptoSign::SigningInterface *handler, Goffset start, Goffset end) { + if (!handler) { + return false; + } const int BUF_SIZE = 65536; unsigned char *buf = new unsigned char[BUF_SIZE]; @@ -593,141 +608,184 @@ static bool hashFileRange(FILE *f, SignatureHandler *handler, Goffset start, Gof delete[] buf; return false; } - handler->updateHash(buf, len); + handler->addData(buf, len); start += len; } delete[] buf; return true; } -#endif -bool FormWidgetSignature::signDocument(const char *saveFilename, const char *certNickname, const char *digestName, const char *password, const GooString *reason, const GooString *location, const std::optional &ownerPassword, - const std::optional &userPassword) +std::optional FormWidgetSignature::signDocument(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location, + const std::optional &ownerPassword, const std::optional &userPassword) { -#ifdef ENABLE_NSS3 - if (!certNickname) { + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + return CryptoSign::SigningError::InternalError; + } + if (certNickname.empty()) { fprintf(stderr, "signDocument: Empty nickname\n"); - return false; + return CryptoSign::SigningError::KeyMissing; } - // calculate a signature over tmp_buffer with the certificate to get its size - unsigned char tmp_buffer[4]; - memcpy(tmp_buffer, "PDF", 4); - SignatureHandler sigHandler(certNickname, SEC_OID_SHA256); - sigHandler.updateHash(tmp_buffer, 4); - const std::unique_ptr tmpSignature = sigHandler.signDetached(password); - if (!tmpSignature) { - return false; - } + auto sigHandler = backend->createSigningHandler(certNickname, HashAlgorithm::Sha256); FormFieldSignature *signatureField = static_cast(field); - std::unique_ptr certInfo = sigHandler.getCertificateInfo(); + std::unique_ptr certInfo = sigHandler->getCertificateInfo(); + if (!certInfo) { + fprintf(stderr, "signDocument: error getting signature info\n"); + return CryptoSign::SigningError::KeyMissing; + } const std::string signerName = certInfo->getSubjectInfo().commonName; signatureField->setCertificateInfo(certInfo); updateWidgetAppearance(); // add visible signing info to appearance Object vObj(new Dict(xref)); Ref vref = xref->addIndirectObject(vObj); - if (!createSignature(vObj, vref, GooString(signerName), tmpSignature.get(), reason, location)) { - return false; + if (!createSignature(vObj, vref, GooString(signerName), CryptoSign::maxSupportedSignatureSize, reason, location, sigHandler->signatureType())) { + return CryptoSign::SigningError::InternalError; } // Incremental save to avoid breaking any existing signatures const GooString fname(saveFilename); if (doc->saveAs(fname, writeForceIncremental) != errNone) { - fprintf(stderr, "signDocument: error saving to file \"%s\"\n", saveFilename); - return false; + fprintf(stderr, "signDocument: error saving to file \"%s\"\n", saveFilename.c_str()); + return CryptoSign::SigningError::WriteFailed; } // Get start/end offset of signature object in the saved PDF Goffset objStart, objEnd; if (!getObjectStartEnd(fname, vref.num, &objStart, &objEnd, ownerPassword, userPassword)) { fprintf(stderr, "signDocument: unable to get signature object offsets\n"); + return CryptoSign::SigningError::InternalError; } // Update byte range of signature in the saved PDF Goffset sigStart, sigEnd, fileSize; - FILE *file = openFile(saveFilename, "r+b"); + FILE *file = openFile(saveFilename.c_str(), "r+b"); if (!updateOffsets(file, objStart, objEnd, &sigStart, &sigEnd, &fileSize)) { fprintf(stderr, "signDocument: unable update byte range\n"); fclose(file); - return false; + return CryptoSign::SigningError::WriteFailed; } // compute hash of byte ranges - sigHandler.restartHash(); - if (!hashFileRange(file, &sigHandler, 0LL, sigStart)) { + if (!hashFileRange(file, sigHandler.get(), 0LL, sigStart)) { fclose(file); - return false; + return CryptoSign::SigningError::InternalError; } - if (!hashFileRange(file, &sigHandler, sigEnd, fileSize)) { + if (!hashFileRange(file, sigHandler.get(), sigEnd, fileSize)) { fclose(file); - return false; + return CryptoSign::SigningError::InternalError; } // and sign it - const std::unique_ptr signature = sigHandler.signDetached(password); - if (!signature) { + auto signature = sigHandler->signDetached(password); + if (std::holds_alternative(signature)) { fclose(file); - return false; + return std::get(signature); + } + + if (std::get>(signature).size() > CryptoSign::maxSupportedSignatureSize) { + error(errInternal, -1, "signature too large\n"); + fclose(file); + return CryptoSign::SigningError::InternalError; } + // pad with zeroes to placeholder length + std::get>(signature).resize(CryptoSign::maxSupportedSignatureSize, '\0'); + // write signature to saved file - if (!updateSignature(file, sigStart, sigEnd, signature.get())) { + if (!updateSignature(file, sigStart, sigEnd, std::get>(signature))) { fprintf(stderr, "signDocument: unable update signature\n"); fclose(file); - return false; + return CryptoSign::SigningError::WriteFailed; } - signatureField->setSignature(*signature); + signatureField->setSignature(std::get>(std::move(signature))); fclose(file); - return true; -#else - return false; -#endif + return {}; +} + +static std::tuple calculateDxDy(int rot, const PDFRectangle *rect) +{ + switch (rot) { + case 90: + return { rect->y2 - rect->y1, rect->x2 - rect->x1 }; + + case 180: + return { rect->x2 - rect->y2, rect->y2 - rect->y1 }; + + case 270: + return { rect->y2 - rect->y1, rect->x2 - rect->x1 }; + + default: // assume rot == 0 + return { rect->x2 - rect->x1, rect->y2 - rect->y1 }; + } } -bool FormWidgetSignature::signDocumentWithAppearance(const char *saveFilename, const char *certNickname, const char *digestName, const char *password, const GooString *reason, const GooString *location, - const std::optional &ownerPassword, const std::optional &userPassword, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, - double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor) +std::optional FormWidgetSignature::signDocumentWithAppearance(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location, + const std::optional &ownerPassword, const std::optional &userPassword, const GooString &signatureText, + const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, + std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor) { // Set the appearance GooString *aux = getField()->getDefaultAppearance(); std::string originalDefaultAppearance = aux ? aux->toStr() : std::string(); Form *form = doc->getCatalog()->getCreateForm(); - std::string pdfFontName = form->findFontInDefaultResources("Helvetica", ""); - if (pdfFontName.empty()) { - pdfFontName = form->addFontToDefaultResources("Helvetica", "").fontName; - } - - const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) }; - getField()->setDefaultAppearance(da.toAppearanceString()); + double x1, y1, x2, y2; + getRect(&x1, &y1, &x2, &y2); + const PDFRectangle rect(x1, y1, x2, y2); std::unique_ptr origAppearCharacs = getWidgetAnnotation()->getAppearCharacs() ? getWidgetAnnotation()->getAppearCharacs()->copy() : nullptr; - auto appearCharacs = std::make_unique(nullptr); - appearCharacs->setBorderColor(std::move(borderColor)); - appearCharacs->setBackColor(std::move(backgroundColor)); - getWidgetAnnotation()->setAppearCharacs(std::move(appearCharacs)); + const int rot = origAppearCharacs ? origAppearCharacs->getRotation() : 0; + const auto dxdy = calculateDxDy(rot, &rect); + const double dx = std::get<0>(dxdy); + const double dy = std::get<1>(dxdy); + const double wMax = dx - 2 * borderWidth - 4; + const double hMax = dy - 2 * borderWidth; std::unique_ptr origBorderCopy = getWidgetAnnotation()->getBorder() ? getWidgetAnnotation()->getBorder()->copy() : nullptr; std::unique_ptr border(new AnnotBorderArray()); border->setWidth(borderWidth); getWidgetAnnotation()->setBorder(std::move(border)); + if (signatureText.getLength() || signatureTextLeft.getLength()) { + const std::string pdfFontName = form->findPdfFontNameToUseForSigning(); + if (pdfFontName.empty()) { + return CryptoSign::SigningError::InternalError; + } + std::shared_ptr font = form->getDefaultResources()->lookupFont(pdfFontName.c_str()); + + if (fontSize == 0) { + fontSize = Annot::calculateFontSize(form, font.get(), &signatureText, wMax / 2.0, hMax); + } + if (leftFontSize == 0) { + leftFontSize = Annot::calculateFontSize(form, font.get(), &signatureTextLeft, wMax / 2.0, hMax); + } + const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) }; + getField()->setDefaultAppearance(da.toAppearanceString()); + form->ensureFontsForAllCharacters(&signatureText, pdfFontName); + form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName); + } + auto appearCharacs = std::make_unique(nullptr); + appearCharacs->setBorderColor(std::move(borderColor)); + appearCharacs->setBackColor(std::move(backgroundColor)); + getWidgetAnnotation()->setAppearCharacs(std::move(appearCharacs)); + getWidgetAnnotation()->generateFieldAppearance(); getWidgetAnnotation()->updateAppearanceStream(); - form->ensureFontsForAllCharacters(&signatureText, pdfFontName); - form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName); - ::FormFieldSignature *ffs = static_cast<::FormFieldSignature *>(getField()); ffs->setCustomAppearanceContent(signatureText); ffs->setCustomAppearanceLeftContent(signatureTextLeft); ffs->setCustomAppearanceLeftFontSize(leftFontSize); - const bool success = signDocument(saveFilename, certNickname, digestName, password, reason, location, ownerPassword, userPassword); + // say that there a now signatures and that we should append only + doc->getCatalog()->getAcroForm()->dictSet("SigFlags", Object(3)); + + auto signingResult = signDocument(saveFilename, certNickname, password, reason, location, ownerPassword, userPassword); // Now bring back the annotation appearance back to what it was ffs->setDefaultAppearance(originalDefaultAppearance); @@ -738,13 +796,13 @@ bool FormWidgetSignature::signDocumentWithAppearance(const char *saveFilename, c getWidgetAnnotation()->generateFieldAppearance(); getWidgetAnnotation()->updateAppearanceStream(); - return success; + return signingResult; } // Get start and end file position of objNum in the PDF named filename. bool FormWidgetSignature::getObjectStartEnd(const GooString &filename, int objNum, Goffset *objStart, Goffset *objEnd, const std::optional &ownerPassword, const std::optional &userPassword) { - PDFDoc newDoc(std::unique_ptr(filename.copy()), ownerPassword, userPassword); + PDFDoc newDoc(filename.copy(), ownerPassword, userPassword); if (!newDoc.isOk()) { return false; } @@ -805,12 +863,12 @@ bool FormWidgetSignature::updateOffsets(FILE *f, Goffset objStart, Goffset objEn } buf[bufSize] = 0; // prevent string functions from searching past the end - // search for the Contents field which contains the signature - // which always must start with hex digits 308 + // search for the Contents field which contains the signature placeholder + // which always must start with hex digits 000 *sigStart = -1; *sigEnd = -1; for (size_t i = 0; i < bufSize - 14; i++) { - if (buf[i] == '/' && strncmp(&buf[i], "/Contents <308", 14) == 0) { + if (buf[i] == '/' && strncmp(&buf[i], "/Contents <000", 14) == 0) { *sigStart = objStart + i + 10; char *p = strchr(&buf[i], '>'); if (p) { @@ -853,33 +911,30 @@ bool FormWidgetSignature::updateOffsets(FILE *f, Goffset objStart, Goffset objEn } // Overwrite signature string in the file with new signature -bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString *signature) +bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const std::vector &signature) { - if (signature->getLength() * 2 + 2 != sigEnd - sigStart) { + if (signature.size() * 2 + 2 != size_t(sigEnd - sigStart)) { return false; } if (Gfseek(f, sigStart, SEEK_SET) != 0) { return false; } - const char *c = signature->c_str(); fprintf(f, "<"); - for (int i = 0; i < signature->getLength(); i++) { - unsigned char value = *(c + i) & 0x000000ff; + for (unsigned char value : signature) { fprintf(f, "%2.2x", value); } fprintf(f, "> "); return true; } -bool FormWidgetSignature::createSignature(Object &vObj, Ref vRef, const GooString &name, const GooString *signature, const GooString *reason, const GooString *location) +bool FormWidgetSignature::createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason, const GooString *location, CryptoSign::SignatureType signatureType) { vObj.dictAdd("Type", Object(objName, "Sig")); vObj.dictAdd("Filter", Object(objName, "Adobe.PPKLite")); - vObj.dictAdd("SubFilter", Object(objName, "adbe.pkcs7.detached")); + vObj.dictAdd("SubFilter", Object(objName, toStdString(signatureType).c_str())); vObj.dictAdd("Name", Object(name.copy())); - GooString *date = timeToDateString(nullptr); - vObj.dictAdd("M", Object(date)); + vObj.dictAdd("M", Object(timeToDateString(nullptr))); if (reason && (reason->getLength() > 0)) { vObj.dictAdd("Reason", Object(reason->copy())); } @@ -887,7 +942,7 @@ bool FormWidgetSignature::createSignature(Object &vObj, Ref vRef, const GooStrin vObj.dictAdd("Location", Object(location->copy())); } - vObj.dictAdd("Contents", Object(objHexString, signature->copy())); + vObj.dictAdd("Contents", Object(objHexString, std::string(placeholderLength, '\0'))); Object bObj(new Array(xref)); // reserve space in byte range for maximum number of bytes bObj.arrayAdd(Object(static_cast(0LL))); @@ -930,16 +985,13 @@ FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *par ref = aref; type = ty; parent = parentA; - numChildren = 0; - children = nullptr; terminal = false; - widgets = nullptr; readOnly = false; - defaultAppearance = nullptr; fullyQualifiedName = nullptr; quadding = VariableTextQuadding::leftJustified; hasQuadding = false; standAlone = false; + noExport = false; // childs Object obj1 = dict->lookup("Kids"); @@ -969,19 +1021,17 @@ FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *par usedParentsAux.insert(childRef.num); if (terminal) { - error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n"); + error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids"); continue; } - numChildren++; - children = (FormField **)greallocn(children, numChildren, sizeof(FormField *)); - children[numChildren - 1] = Form::createFieldFromDict(std::move(childObj), doc, childRef, this, &usedParentsAux); + children.push_back(Form::createFieldFromDict(std::move(childObj), doc, childRef, this, &usedParentsAux)); } else { Object obj2 = childObj.dictLookup("Subtype"); if (obj2.isName("Widget")) { // Child is a widget annotation - if (!terminal && numChildren > 0) { - error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids\n"); + if (!terminal && !children.empty()) { + error(errSyntaxWarning, -1, "Field can't have both Widget AND Field as kids"); continue; } _createWidget(&childObj, childRef); @@ -1008,7 +1058,7 @@ FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *par // TODO } if (flags & 0x4) { // 3 -> NoExport - // TODO + noExport = true; } } @@ -1030,78 +1080,51 @@ FormField::FormField(PDFDoc *docA, Object &&aobj, const Ref aref, FormField *par obj1 = dict->lookup("T"); if (obj1.isString()) { partialName = obj1.getString()->copy(); - } else { - partialName = nullptr; } obj1 = dict->lookup("TU"); if (obj1.isString()) { alternateUiName = obj1.getString()->copy(); - } else { - alternateUiName = nullptr; } obj1 = dict->lookup("TM"); if (obj1.isString()) { mappingName = obj1.getString()->copy(); - } else { - mappingName = nullptr; } + widgets.shrink_to_fit(); + children.shrink_to_fit(); } void FormField::setDefaultAppearance(const std::string &appearance) { - delete defaultAppearance; - defaultAppearance = new GooString(appearance); + defaultAppearance = std::make_unique(appearance); } void FormField::setPartialName(const GooString &name) { - delete partialName; partialName = name.copy(); obj.getDict()->set("T", Object(name.copy())); xref->setModifiedObject(&obj, ref); } -FormField::~FormField() -{ - if (!terminal) { - if (children) { - for (int i = 0; i < numChildren; i++) { - delete children[i]; - } - gfree(children); - } - } else { - for (int i = 0; i < numChildren; ++i) { - delete widgets[i]; - } - gfree(widgets); - } - - delete defaultAppearance; - delete partialName; - delete alternateUiName; - delete mappingName; - delete fullyQualifiedName; -} +FormField::~FormField() = default; void FormField::print(int indent) { - printf("%*s- (%d %d): [container] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); + printf("%*s- (%d %d): [container] terminal: %s children: %zu\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", terminal ? widgets.size() : children.size()); } void FormField::printTree(int indent) { print(indent); if (terminal) { - for (int i = 0; i < numChildren; i++) { - widgets[i]->print(indent + 4); + for (const auto &widget : widgets) { + widget->print(indent + 4); } } else { - for (int i = 0; i < numChildren; i++) { - children[i]->printTree(indent + 4); + for (const auto &child : children) { + child->printTree(indent + 4); } } } @@ -1111,20 +1134,20 @@ void FormField::fillChildrenSiblingsID() if (terminal) { return; } - for (int i = 0; i < numChildren; i++) { - children[i]->fillChildrenSiblingsID(); + for (const auto &child : children) { + child->fillChildrenSiblingsID(); } } void FormField::createWidgetAnnotations() { if (terminal) { - for (int i = 0; i < numChildren; i++) { - widgets[i]->createWidgetAnnotation(); + for (auto &widget : widgets) { + widget->createWidgetAnnotation(); } } else { - for (int i = 0; i < numChildren; i++) { - children[i]->createWidgetAnnotations(); + for (auto &child : children) { + child->createWidgetAnnotations(); } } } @@ -1132,39 +1155,36 @@ void FormField::createWidgetAnnotations() void FormField::_createWidget(Object *objA, Ref aref) { terminal = true; - numChildren++; - widgets = (FormWidget **)greallocn(widgets, numChildren, sizeof(FormWidget *)); // ID = index in "widgets" table switch (type) { case formButton: - widgets[numChildren - 1] = new FormWidgetButton(doc, objA, numChildren - 1, aref, this); + widgets.push_back(std::make_unique(doc, objA, widgets.size(), aref, this)); break; case formText: - widgets[numChildren - 1] = new FormWidgetText(doc, objA, numChildren - 1, aref, this); + widgets.push_back(std::make_unique(doc, objA, widgets.size(), aref, this)); break; case formChoice: - widgets[numChildren - 1] = new FormWidgetChoice(doc, objA, numChildren - 1, aref, this); + widgets.push_back(std::make_unique(doc, objA, widgets.size(), aref, this)); break; case formSignature: - widgets[numChildren - 1] = new FormWidgetSignature(doc, objA, numChildren - 1, aref, this); + widgets.push_back(std::make_unique(doc, objA, widgets.size(), aref, this)); break; default: error(errSyntaxWarning, -1, "SubType on non-terminal field, invalid document?"); - numChildren--; } } FormWidget *FormField::findWidgetByRef(Ref aref) { if (terminal) { - for (int i = 0; i < numChildren; i++) { - if (widgets[i]->getRef() == aref) { - return widgets[i]; + for (auto &widget : widgets) { + if (widget->getRef() == aref) { + return widget.get(); } } } else { - for (int i = 0; i < numChildren; i++) { - FormWidget *result = children[i]->findWidgetByRef(aref); + for (auto &child : children) { + FormWidget *result = child->findWidgetByRef(aref); if (result) { return result; } @@ -1173,101 +1193,103 @@ FormWidget *FormField::findWidgetByRef(Ref aref) return nullptr; } -GooString *FormField::getFullyQualifiedName() +const GooString *FormField::getFullyQualifiedName() const { - Object obj1; Object parentObj; const GooString *parent_name; - GooString *full_name; bool unicode_encoded = false; if (fullyQualifiedName) { - return fullyQualifiedName; + return fullyQualifiedName.get(); } - full_name = new GooString(); + fullyQualifiedName = std::make_unique(); - obj1 = obj.copy(); - while (parentObj = obj1.dictLookup("Parent"), parentObj.isDict()) { + std::set parsedRefs; + Ref parentRef; + parentObj = obj.getDict()->lookup("Parent", &parentRef); + if (parentRef != Ref::INVALID()) { + parsedRefs.insert(parentRef.num); + } + while (parentObj.isDict()) { Object obj2 = parentObj.dictLookup("T"); if (obj2.isString()) { parent_name = obj2.getString(); if (unicode_encoded) { - full_name->insert(0, "\0.", 2); // 2-byte unicode period - if (parent_name->hasUnicodeMarker()) { - full_name->insert(0, parent_name->c_str() + 2, parent_name->getLength() - 2); // Remove the unicode BOM + fullyQualifiedName->insert(0, "\0.", 2); // 2-byte unicode period + if (hasUnicodeByteOrderMark(parent_name->toStr())) { + fullyQualifiedName->insert(0, parent_name->c_str() + 2, parent_name->getLength() - 2); // Remove the unicode BOM } else { - int tmp_length; - char *tmp_str = pdfDocEncodingToUTF16(parent_name->toStr(), &tmp_length); - full_name->insert(0, tmp_str + 2, tmp_length - 2); // Remove the unicode BOM - delete[] tmp_str; + std::string tmp_str = pdfDocEncodingToUTF16(parent_name->toStr()); + fullyQualifiedName->insert(0, tmp_str.c_str() + 2, tmp_str.size() - 2); // Remove the unicode BOM } } else { - full_name->insert(0, '.'); // 1-byte ascii period - if (parent_name->hasUnicodeMarker()) { + fullyQualifiedName->insert(0, '.'); // 1-byte ascii period + if (hasUnicodeByteOrderMark(parent_name->toStr())) { unicode_encoded = true; - full_name = convertToUtf16(full_name); - full_name->insert(0, parent_name->c_str() + 2, parent_name->getLength() - 2); // Remove the unicode BOM + fullyQualifiedName = convertToUtf16(fullyQualifiedName.get()); + fullyQualifiedName->insert(0, parent_name->c_str() + 2, parent_name->getLength() - 2); // Remove the unicode BOM } else { - full_name->insert(0, parent_name); + fullyQualifiedName->insert(0, parent_name); } } } - obj1 = parentObj.copy(); + parentObj = parentObj.getDict()->lookup("Parent", &parentRef); + if (parentRef != Ref::INVALID() && !parsedRefs.insert(parentRef.num).second) { + error(errSyntaxError, -1, "FormField: Loop while trying to look for Parents"); + return fullyQualifiedName.get(); + } } if (partialName) { if (unicode_encoded) { - if (partialName->hasUnicodeMarker()) { - full_name->append(partialName->c_str() + 2, partialName->getLength() - 2); // Remove the unicode BOM + if (hasUnicodeByteOrderMark(partialName->toStr())) { + fullyQualifiedName->append(partialName->c_str() + 2, partialName->getLength() - 2); // Remove the unicode BOM } else { - int tmp_length; - char *tmp_str = pdfDocEncodingToUTF16(partialName->toStr(), &tmp_length); - full_name->append(tmp_str + 2, tmp_length - 2); // Remove the unicode BOM - delete[] tmp_str; + std::string tmp_str = pdfDocEncodingToUTF16(partialName->toStr()); + fullyQualifiedName->append(tmp_str.c_str() + 2, tmp_str.size() - 2); // Remove the unicode BOM } } else { - if (partialName->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(partialName->toStr())) { unicode_encoded = true; - full_name = convertToUtf16(full_name); - full_name->append(partialName->c_str() + 2, partialName->getLength() - 2); // Remove the unicode BOM + fullyQualifiedName = convertToUtf16(fullyQualifiedName.get()); + fullyQualifiedName->append(partialName->c_str() + 2, partialName->getLength() - 2); // Remove the unicode BOM } else { - full_name->append(partialName); + fullyQualifiedName->append(partialName.get()); } } } else { - int len = full_name->getLength(); + int len = fullyQualifiedName->getLength(); // Remove the last period if (unicode_encoded) { if (len > 1) { - full_name->del(len - 2, 2); + fullyQualifiedName->del(len - 2, 2); } } else { if (len > 0) { - full_name->del(len - 1, 1); + fullyQualifiedName->del(len - 1, 1); } } } if (unicode_encoded) { - full_name->prependUnicodeMarker(); + prependUnicodeByteOrderMark(fullyQualifiedName->toNonConstStr()); } - fullyQualifiedName = full_name; - return fullyQualifiedName; + return fullyQualifiedName.get(); } void FormField::updateChildrenAppearance() { // Recursively update each child's appearance if (terminal) { - for (int i = 0; i < numChildren; i++) { - widgets[i]->updateWidgetAppearance(); + for (auto &widget : widgets) { + widget->updateWidgetAppearance(); } } else { - for (int i = 0; i < numChildren; i++) { - children[i]->updateChildrenAppearance(); + for (auto &child : children) { + child->updateChildrenAppearance(); } } } @@ -1306,8 +1328,8 @@ void FormField::reset(const std::vector &excludedFields) void FormField::resetChildren(const std::vector &excludedFields) { if (!terminal) { - for (int i = 0; i < numChildren; i++) { - children[i]->reset(excludedFields); + for (auto &child : children) { + child->reset(excludedFields); } } } @@ -1338,8 +1360,8 @@ FormField *FormField::findFieldByRef(Ref aref) return this; } } else { - for (int i = 0; i < numChildren; i++) { - FormField *result = children[i]->findFieldByRef(aref); + for (auto &child : children) { + FormField *result = child->findFieldByRef(aref); if (result) { return result; } @@ -1355,8 +1377,8 @@ FormField *FormField::findFieldByFullyQualifiedName(const std::string &name) return this; } } else { - for (int i = 0; i < numChildren; i++) { - FormField *result = children[i]->findFieldByFullyQualifiedName(name); + for (auto &child : children) { + FormField *result = child->findFieldByFullyQualifiedName(name); if (result) { return result; } @@ -1373,8 +1395,6 @@ FormFieldButton::FormFieldButton(PDFDoc *docA, Object &&dictObj, const Ref refA, Dict *dict = obj.getDict(); active_child = -1; noAllOff = false; - siblings = nullptr; - numSiblings = 0; appearanceState.setToNull(); defaultAppearanceState.setToNull(); @@ -1392,7 +1412,7 @@ FormFieldButton::FormFieldButton(PDFDoc *docA, Object &&dictObj, const Ref refA, } } if (flags & 0x1000000) { // 26 -> radiosInUnison - error(errUnimplemented, -1, "FormFieldButton:: radiosInUnison flag unimplemented, please report a bug with a testcase\n"); + error(errUnimplemented, -1, "FormFieldButton:: radiosInUnison flag unimplemented, please report a bug with a testcase"); } } @@ -1424,25 +1444,25 @@ static const char *_getButtonType(FormButtonType type) void FormFieldButton::print(int indent) { - printf("%*s- (%d %d): [%s] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, _getButtonType(btype), terminal ? "Yes" : "No", numChildren); + printf("%*s- (%d %d): [%s] terminal: %s children: %zu\n", indent, "", ref.num, ref.gen, _getButtonType(btype), terminal ? "Yes" : "No", terminal ? widgets.size() : children.size()); } void FormFieldButton::setNumSiblings(int num) { - numSiblings = num; - siblings = (FormFieldButton **)greallocn(siblings, numSiblings, sizeof(FormFieldButton *)); + siblings.resize(num, nullptr); } void FormFieldButton::fillChildrenSiblingsID() { if (!terminal) { + int numChildren = int(children.size()); for (int i = 0; i < numChildren; i++) { - FormFieldButton *child = dynamic_cast(children[i]); + FormFieldButton *child = dynamic_cast(children[i].get()); if (child != nullptr) { // Fill the siblings of this node childs child->setNumSiblings(numChildren - 1); for (int j = 0, counter = 0; j < numChildren; j++) { - FormFieldButton *otherChild = dynamic_cast(children[j]); + FormFieldButton *otherChild = dynamic_cast(children[j].get()); if (i == j) { continue; } @@ -1486,14 +1506,14 @@ bool FormFieldButton::setState(const char *state, bool ignoreToggleOff) const char *current = getAppearanceState(); bool currentFound = false, newFound = false; - for (int i = 0; i < numChildren; i++) { + for (int i = 0; i < int(terminal ? widgets.size() : children.size()); i++) { FormWidgetButton *widget; // If radio button is a terminal field we want the widget at i, but // if it's not terminal, the child widget is a composed dict, so // we want the ony child widget of the children at i if (terminal) { - widget = static_cast(widgets[i]); + widget = static_cast(widgets[i].get()); } else { widget = static_cast(children[i]->getWidget(0)); } @@ -1542,12 +1562,7 @@ void FormFieldButton::updateState(const char *state) xref->setModifiedObject(&obj, ref); } -FormFieldButton::~FormFieldButton() -{ - if (siblings) { - gfree(siblings); - } -} +FormFieldButton::~FormFieldButton() = default; void FormFieldButton::reset(const std::vector &excludedFields) { @@ -1575,9 +1590,6 @@ FormFieldText::FormFieldText(PDFDoc *docA, Object &&dictObj, const Ref refA, For { Dict *dict = obj.getDict(); Object obj1; - content = nullptr; - internalContent = nullptr; - defaultContent = nullptr; multiline = password = fileSelect = doNotSpellCheck = doNotScroll = comb = richText = false; maxLen = 0; @@ -1623,7 +1635,7 @@ void FormFieldText::fillContent(FillValueType fillType) obj1 = Form::fieldLookup(dict, fillType == fillDefaultValue ? "DV" : "V"); if (obj1.isString()) { - if (obj1.getString()->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(obj1.getString()->toStr())) { if (obj1.getString()->getLength() > 2) { if (fillType == fillDefaultValue) { defaultContent = obj1.getString()->copy(); @@ -1633,40 +1645,36 @@ void FormFieldText::fillContent(FillValueType fillType) } } else if (obj1.getString()->getLength() > 0) { // non-unicode string -- assume pdfDocEncoding and try to convert to UTF16BE - int tmp_length; - char *tmp_str = pdfDocEncodingToUTF16(obj1.getString()->toStr(), &tmp_length); + std::string tmp_str = pdfDocEncodingToUTF16(obj1.getString()->toStr()); if (fillType == fillDefaultValue) { - defaultContent = new GooString(tmp_str, tmp_length); + defaultContent = std::make_unique(std::move(tmp_str)); } else { - content = new GooString(tmp_str, tmp_length); + content = std::make_unique(std::move(tmp_str)); } - - delete[] tmp_str; } } } void FormFieldText::print(int indent) { - printf("%*s- (%d %d): [text] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); + printf("%*s- (%d %d): [text] terminal: %s children: %zu\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", terminal ? widgets.size() : children.size()); } -void FormFieldText::setContentCopy(const GooString *new_content) +void FormFieldText::setContent(std::unique_ptr new_content) { - delete content; - content = nullptr; + content.reset(); if (new_content) { - content = new_content->copy(); + content = std::move(new_content); // append the unicode marker if needed - if (!content->hasUnicodeMarker()) { - content->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(content->toStr())) { + prependUnicodeByteOrderMark(content->toNonConstStr()); } Form *form = doc->getCatalog()->getForm(); if (form) { - DefaultAppearance da(defaultAppearance); + DefaultAppearance da(defaultAppearance.get()); if (da.getFontName().isName()) { const std::string fontName = da.getFontName().getName(); if (!fontName.empty()) { @@ -1674,14 +1682,14 @@ void FormFieldText::setContentCopy(const GooString *new_content) Object fieldResourcesDictObj = obj.dictLookup("DR"); if (fieldResourcesDictObj.isDict()) { GfxResources fieldResources(doc->getXRef(), fieldResourcesDictObj.getDict(), form->getDefaultResources()); - const std::vector newFonts = form->ensureFontsForAllCharacters(content, fontName, &fieldResources); + const std::vector newFonts = form->ensureFontsForAllCharacters(content.get(), fontName, &fieldResources); // If we added new fonts to the Form object default resuources we also need to add them (we only add the ref so this is cheap) // to the field DR dictionary for (const Form::AddFontResult &afr : newFonts) { fieldResourcesDictObj.dictLookup("Font").dictAdd(afr.fontName.c_str(), Object(afr.ref)); } } else { - form->ensureFontsForAllCharacters(content, fontName); + form->ensureFontsForAllCharacters(content.get(), fontName); } } } else { @@ -1690,33 +1698,27 @@ void FormFieldText::setContentCopy(const GooString *new_content) } } - obj.getDict()->set("V", Object(content ? content->copy() : new GooString(""))); + obj.getDict()->set("V", Object(content ? content->copy() : std::make_unique(""))); xref->setModifiedObject(&obj, ref); updateChildrenAppearance(); } -void FormFieldText::setAppearanceContentCopy(const GooString *new_content) +void FormFieldText::setAppearanceContent(std::unique_ptr new_content) { - delete internalContent; - internalContent = nullptr; + internalContent.reset(); if (new_content) { - internalContent = new_content->copy(); + internalContent = std::move(new_content); } updateChildrenAppearance(); } -FormFieldText::~FormFieldText() -{ - delete content; - delete internalContent; - delete defaultContent; -} +FormFieldText::~FormFieldText() = default; void FormFieldText::reset(const std::vector &excludedFields) { if (!isAmongExcludedFields(excludedFields)) { - setContentCopy(defaultContent); + setContent(defaultContent ? defaultContent->copy() : nullptr); if (defaultContent == nullptr) { obj.getDict()->remove("V"); } @@ -1746,13 +1748,10 @@ void FormFieldText::setTextFontSize(int fontSize) std::vector daToks; int idx = parseDA(&daToks); if (idx == -1) { - error(errSyntaxError, -1, "FormFieldText:: invalid DA object\n"); + error(errSyntaxError, -1, "FormFieldText:: invalid DA object"); return; } - if (defaultAppearance) { - delete defaultAppearance; - } - defaultAppearance = new GooString; + defaultAppearance = std::make_unique(); for (std::size_t i = 0; i < daToks.size(); ++i) { if (i > 0) { defaultAppearance->append(' '); @@ -1815,6 +1814,7 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, Fo choices = nullptr; defaultChoices = nullptr; editedChoice = nullptr; + appearanceSelectedChoice = nullptr; topIdx = 0; Dict *dict = obj.getDict(); @@ -1846,7 +1846,7 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, Fo if (obj1.isInt()) { topIdx = obj1.getInt(); if (topIdx < 0) { - error(errSyntaxError, -1, "FormFieldChoice:: invalid topIdx entry\n"); + error(errSyntaxError, -1, "FormFieldChoice:: invalid topIdx entry"); topIdx = 0; } } @@ -1855,7 +1855,6 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, Fo if (obj1.isArray()) { numChoices = obj1.arrayGetLength(); choices = new ChoiceOpt[numChoices]; - memset(choices, 0, sizeof(ChoiceOpt) * numChoices); for (int i = 0; i < numChoices; i++) { Object obj2 = obj1.arrayGet(i); @@ -1863,24 +1862,24 @@ FormFieldChoice::FormFieldChoice(PDFDoc *docA, Object &&aobj, const Ref refA, Fo choices[i].optionName = obj2.getString()->copy(); } else if (obj2.isArray()) { // [Export_value, Displayed_text] if (obj2.arrayGetLength() < 2) { - error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- array's length < 2\n"); + error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- array's length < 2"); continue; } Object obj3 = obj2.arrayGet(0); if (obj3.isString()) { choices[i].exportVal = obj3.getString()->copy(); } else { - error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- exported value not a string\n"); + error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- exported value not a string"); } obj3 = obj2.arrayGet(1); if (obj3.isString()) { choices[i].optionName = obj3.getString()->copy(); } else { - error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- choice name not a string\n"); + error(errSyntaxError, -1, "FormWidgetChoice:: invalid Opt entry -- choice name not a string"); } } else { - error(errSyntaxError, -1, "FormWidgetChoice:: invalid {0:d} Opt entry\n", i); + error(errSyntaxError, -1, "FormWidgetChoice:: invalid {0:d} Opt entry", i); } } } else { @@ -1986,18 +1985,13 @@ void FormFieldChoice::fillChoices(FillValueType fillType) FormFieldChoice::~FormFieldChoice() { - for (int i = 0; i < numChoices; i++) { - delete choices[i].exportVal; - delete choices[i].optionName; - } delete[] choices; delete[] defaultChoices; - delete editedChoice; } void FormFieldChoice::print(int indent) { - printf("%*s- (%d %d): [choice] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); + printf("%*s- (%d %d): [choice] terminal: %s children: %zu\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", terminal ? widgets.size() : children.size()); } void FormFieldChoice::updateSelection() @@ -2018,7 +2012,7 @@ void FormFieldChoice::updateSelection() if (numSelected == 0) { // No options are selected - objV = Object(new GooString("")); + objV = Object(std::make_unique("")); } else if (numSelected == 1) { // Only one option is selected for (int i = 0; i < numChoices; i++) { @@ -2070,8 +2064,7 @@ void FormFieldChoice::unselectAll() void FormFieldChoice::deselectAll() { - delete editedChoice; - editedChoice = nullptr; + editedChoice.reset(); unselectAll(); updateSelection(); @@ -2079,8 +2072,7 @@ void FormFieldChoice::deselectAll() void FormFieldChoice::toggle(int i) { - delete editedChoice; - editedChoice = nullptr; + editedChoice.reset(); choices[i].selected = !choices[i].selected; updateSelection(); @@ -2088,8 +2080,7 @@ void FormFieldChoice::toggle(int i) void FormFieldChoice::select(int i) { - delete editedChoice; - editedChoice = nullptr; + editedChoice.reset(); if (!multiselect) { unselectAll(); @@ -2099,27 +2090,41 @@ void FormFieldChoice::select(int i) updateSelection(); } -void FormFieldChoice::setEditChoice(const GooString *new_content) +void FormFieldChoice::setEditChoice(std::unique_ptr new_content) { - delete editedChoice; - editedChoice = nullptr; + editedChoice.reset(); unselectAll(); if (new_content) { - editedChoice = new_content->copy(); + editedChoice = std::move(new_content); // append the unicode marker if needed - if (!editedChoice->hasUnicodeMarker()) { - editedChoice->prependUnicodeMarker(); + if (!hasUnicodeByteOrderMark(editedChoice->toStr())) { + prependUnicodeByteOrderMark(editedChoice->toNonConstStr()); } } updateSelection(); } +void FormFieldChoice::setAppearanceChoiceContent(std::unique_ptr new_content) +{ + appearanceSelectedChoice.reset(); + + if (new_content) { + appearanceSelectedChoice = std::move(new_content); + + // append the unicode marker if needed + if (!hasUnicodeByteOrderMark(appearanceSelectedChoice->toStr())) { + prependUnicodeByteOrderMark(appearanceSelectedChoice->toNonConstStr()); + } + } + updateChildrenAppearance(); +} + const GooString *FormFieldChoice::getEditChoice() const { - return editedChoice; + return editedChoice.get(); } int FormFieldChoice::getNumSelected() @@ -2136,12 +2141,12 @@ int FormFieldChoice::getNumSelected() const GooString *FormFieldChoice::getSelectedChoice() const { if (edit && editedChoice) { - return editedChoice; + return editedChoice.get(); } for (int i = 0; i < numChoices; i++) { if (choices[i].optionName && choices[i].selected) { - return choices[i].optionName; + return choices[i].optionName.get(); } } @@ -2151,8 +2156,7 @@ const GooString *FormFieldChoice::getSelectedChoice() const void FormFieldChoice::reset(const std::vector &excludedFields) { if (!isAmongExcludedFields(excludedFields)) { - delete editedChoice; - editedChoice = nullptr; + editedChoice.reset(); if (defaultChoices) { for (int i = 0; i < numChoices; i++) { @@ -2172,7 +2176,7 @@ void FormFieldChoice::reset(const std::vector &excludedFields) // FormFieldSignature //------------------------------------------------------------------------ FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref refA, FormField *parentA, std::set *usedParents) - : FormField(docA, std::move(dict), refA, parentA, usedParents, formSignature), signature_type(unsigned_signature_field), signature(nullptr) + : FormField(docA, std::move(dict), refA, parentA, usedParents, formSignature), signature_type(CryptoSign::SignatureType::unsigned_signature_field) { signature_info = new SignatureInfo(); parseInfo(); @@ -2181,13 +2185,11 @@ FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref re FormFieldSignature::~FormFieldSignature() { delete signature_info; - delete signature; } -void FormFieldSignature::setSignature(const GooString &sig) +void FormFieldSignature::setSignature(std::vector &&sig) { - delete signature; - signature = sig.copy(); + signature = std::move(sig); } const GooString &FormFieldSignature::getCustomAppearanceContent() const @@ -2261,7 +2263,8 @@ void FormFieldSignature::parseInfo() Object contents_obj = sig_dict.dictLookup("Contents"); if (contents_obj.isString()) { - signature = contents_obj.getString()->copy(); + auto signatureString = contents_obj.getString(); + signature = std::vector(signatureString->c_str(), signatureString->c_str() + signatureString->getLength()); } byte_range = sig_dict.dictLookup("ByteRange"); @@ -2285,23 +2288,27 @@ void FormFieldSignature::parseInfo() // check if subfilter is supported for signature validation, only detached signatures work for now Object subfilterName = sig_dict.dictLookup("SubFilter"); - if (subfilterName.isName("adbe.pkcs7.sha1")) { - signature_type = adbe_pkcs7_sha1; - signature_info->setSubFilterSupport(true); - } else if (subfilterName.isName("adbe.pkcs7.detached")) { - signature_type = adbe_pkcs7_detached; - signature_info->setSubFilterSupport(true); - } else if (subfilterName.isName("ETSI.CAdES.detached")) { - signature_type = ETSI_CAdES_detached; - signature_info->setSubFilterSupport(true); - } else { - signature_type = unknown_signature_type; + if (subfilterName.getType() == objName && subfilterName.getName()) { + signature_type = CryptoSign::signatureTypeFromString(subfilterName.getName()); + switch (signature_type) { + case CryptoSign::SignatureType::adbe_pkcs7_sha1: + case CryptoSign::SignatureType::adbe_pkcs7_detached: + case CryptoSign::SignatureType::ETSI_CAdES_detached: + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + signature_info->setSubFilterSupport(true); + break; + case CryptoSign::SignatureType::unknown_signature_type: + case CryptoSign::SignatureType::unsigned_signature_field: + break; + } } } -void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset block_len) +void FormFieldSignature::hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len) { -#ifdef ENABLE_NSS3 + if (!handler) { + return; + } const int BLOCK_SIZE = 4096; unsigned char signed_data_buffer[BLOCK_SIZE]; @@ -2310,54 +2317,75 @@ void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset Goffset bytes_left = block_len - i; if (bytes_left < BLOCK_SIZE) { doc->getBaseStream()->doGetChars(static_cast(bytes_left), signed_data_buffer); - handler->updateHash(signed_data_buffer, static_cast(bytes_left)); + handler->addData(signed_data_buffer, static_cast(bytes_left)); i = block_len; } else { doc->getBaseStream()->doGetChars(BLOCK_SIZE, signed_data_buffer); - handler->updateHash(signed_data_buffer, BLOCK_SIZE); + handler->addData(signed_data_buffer, BLOCK_SIZE); i += BLOCK_SIZE; } } -#endif } -FormSignatureType FormWidgetSignature::signatureType() const +CryptoSign::SignatureType FormWidgetSignature::signatureType() const { return static_cast(field)->getSignatureType(); } -void FormWidgetSignature::setSignatureType(FormSignatureType fst) +void FormWidgetSignature::setSignatureType(CryptoSign::SignatureType fst) { static_cast(field)->setSignatureType(fst); } -SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA) +SignatureInfo *FormFieldSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback) { -#ifdef ENABLE_NSS3 + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + if (doneCallback) { + doneCallback(); + } + return signature_info; + } + if (signature_info->getSignatureValStatus() != SIGNATURE_NOT_VERIFIED && !forceRevalidation) { + if (doneCallback) { + doneCallback(); + } return signature_info; } - if (signature == nullptr) { + if (signature.empty()) { error(errSyntaxError, 0, "Invalid or missing Signature string"); + if (doneCallback) { + doneCallback(); + } return signature_info; } if (!byte_range.isArray()) { error(errSyntaxError, 0, "Invalid or missing ByteRange array"); + if (doneCallback) { + doneCallback(); + } return signature_info; } int arrayLen = byte_range.arrayGetLength(); if (arrayLen < 2) { error(errSyntaxError, 0, "Too few elements in ByteRange array"); + if (doneCallback) { + doneCallback(); + } return signature_info; } - const int signature_len = signature->getLength(); - unsigned char *signatureuchar = (unsigned char *)gmalloc(signature_len); - memcpy(signatureuchar, signature->c_str(), signature_len); - SignatureHandler signature_handler(signatureuchar, signature_len); + signature_handler = backend->createVerificationHandler(std::vector(signature), signature_type); + if (!signature_handler) { + if (doneCallback) { + doneCallback(); + } + return signature_info; + } Goffset fileLength = doc->getBaseStream()->getLength(); for (int i = 0; i < arrayLen / 2; i++) { @@ -2366,6 +2394,9 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for if (!offsetObj.isIntOrInt64() || !lenObj.isIntOrInt64()) { error(errSyntaxError, 0, "Illegal values in ByteRange array"); + if (doneCallback) { + doneCallback(); + } return signature_info; } @@ -2374,42 +2405,56 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for if (offset < 0 || offset >= fileLength || len < 0 || len > fileLength || offset + len > fileLength) { error(errSyntaxError, 0, "Illegal values in ByteRange array"); + if (doneCallback) { + doneCallback(); + } return signature_info; } doc->getBaseStream()->setPos(offset); - hashSignedDataBlock(&signature_handler, len); + hashSignedDataBlock(signature_handler.get(), len); } - signature_info->setSignerName(signature_handler.getSignerName().c_str()); - signature_info->setSubjectDN(signature_handler.getSignerSubjectDN()); - signature_info->setHashAlgorithm(signature_handler.getHashAlgorithm()); - if (!signature_info->isSubfilterSupported()) { error(errUnimplemented, 0, "Unable to validate this type of signature"); + if (doneCallback) { + doneCallback(); + } return signature_info; } - - const SignatureValidationStatus sig_val_state = signature_handler.validateSignature(); + const SignatureValidationStatus sig_val_state = signature_handler->validateSignature(); signature_info->setSignatureValStatus(sig_val_state); + signature_info->setSignerName(signature_handler->getSignerName()); + signature_info->setSubjectDN(signature_handler->getSignerSubjectDN()); + signature_info->setHashAlgorithm(signature_handler->getHashAlgorithm()); // verify if signature contains a 'signing time' attribute - if (signature_handler.getSigningTime() != 0) { - signature_info->setSigningTime(signature_handler.getSigningTime()); + if (signature_handler->getSigningTime() != std::chrono::system_clock::time_point {}) { + signature_info->setSigningTime(std::chrono::system_clock::to_time_t(signature_handler->getSigningTime())); } + signature_info->setCertificateInfo(signature_handler->getCertificateInfo()); + if (sig_val_state != SIGNATURE_VALID || !doVerifyCert) { + if (doneCallback) { + doneCallback(); + } return signature_info; } - const CertificateValidationStatus cert_val_state = signature_handler.validateCertificate(validationTime, ocspRevocationCheck, enableAIA); - signature_info->setCertificateValStatus(cert_val_state); - signature_info->setCertificateInfo(signature_handler.getCertificateInfo()); + signature_handler->validateCertificateAsync(std::chrono::system_clock::from_time_t(validationTime), ocspRevocationCheck, enableAIA, doneCallback); -#endif return signature_info; } +CertificateValidationStatus FormFieldSignature::validateSignatureResult() +{ + if (!signature_handler) { + return CERTIFICATE_GENERIC_ERROR; + } + return signature_handler->validateCertificateResult(); +} + std::vector FormFieldSignature::getSignedRangeBounds() const { std::vector range_vec; @@ -2467,63 +2512,68 @@ std::optional FormFieldSignature::getCheckedSignature(Goffset *checke } gstr.append(static_cast(c1)); } while (++pos < len); - if (gstr.getChar(0) == '3' && gstr.getChar(1) == '0') { - if (gstr.getChar(2) == '8' && gstr.getChar(3) == '0') { - // ASN1 DER indefinite length encoding: - // We only check that all characters up to the enclosing '>' - // are hex characters and that there are two hex encoded 0 bytes - // just before the enclosing '>' marking the end of the indefinite - // length encoding. - int paddingCount = 0; - while (gstr.getChar(len - 1) == '0' && gstr.getChar(len - 2) == '0') { - ++paddingCount; - len -= 2; - } - if (paddingCount < 2 || len % 2 == 1) { - len = 0; - } - } else if (gstr.getChar(2) == '8') { - // ASN1 DER definite length encoding: - // We calculate the length of the following bytes from the length bytes and - // check that after the length bytes and the following calculated number of - // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes. - int lenBytes = gstr.getChar(3) - '0'; - if (lenBytes > 0 && lenBytes <= 4) { - int sigLen = 0; - for (int i = 0; i < 2 * lenBytes; ++i) { - sigLen <<= 4; - char c = gstr.getChar(i + 4); - if (isdigit(c)) { - sigLen += c - '0'; - } else if (isxdigit(c) && c >= 'a') { - sigLen += c - 'a' + 10; - } else if (isxdigit(c) && c >= 'A') { - sigLen += c - 'A' + 10; - } else { - len = 0; - break; - } + if (signature_type == CryptoSign::SignatureType::g10c_pgp_signature_detached) { + // Padding here is done as pgp packets, so keep no need to try to unmangle it + return gstr; + } else { + if (gstr.getChar(0) == '3' && gstr.getChar(1) == '0') { + if (gstr.getChar(2) == '8' && gstr.getChar(3) == '0') { + // ASN1 DER indefinite length encoding: + // We only check that all characters up to the enclosing '>' + // are hex characters and that there are two hex encoded 0 bytes + // just before the enclosing '>' marking the end of the indefinite + // length encoding. + int paddingCount = 0; + while (gstr.getChar(len - 1) == '0' && gstr.getChar(len - 2) == '0') { + ++paddingCount; + len -= 2; } - if (sigLen > 0 && 2 * (sigLen + lenBytes) <= len - 4) { - for (Goffset i = 2 * (sigLen + lenBytes) + 4; i < len; ++i) { - if (gstr.getChar(i) != '0') { + if (paddingCount < 2 || len % 2 == 1) { + len = 0; + } + } else if (gstr.getChar(2) == '8') { + // ASN1 DER definite length encoding: + // We calculate the length of the following bytes from the length bytes and + // check that after the length bytes and the following calculated number of + // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes. + int lenBytes = gstr.getChar(3) - '0'; + if (lenBytes > 0 && lenBytes <= 4) { + int sigLen = 0; + for (int i = 0; i < 2 * lenBytes; ++i) { + sigLen <<= 4; + char c = gstr.getChar(i + 4); + if (isdigit(c)) { + sigLen += c - '0'; + } else if (isxdigit(c) && c >= 'a') { + sigLen += c - 'a' + 10; + } else if (isxdigit(c) && c >= 'A') { + sigLen += c - 'A' + 10; + } else { len = 0; break; } } - } else { + if (sigLen > 0 && 2 * (sigLen + lenBytes) <= len - 4) { + for (Goffset i = 2 * (sigLen + lenBytes) + 4; i < len; ++i) { + if (gstr.getChar(i) != '0') { + len = 0; + break; + } + } + } else { + len = 0; + } + } + } + for (const char c : gstr.toStr()) { + if (!isxdigit(c)) { len = 0; } } - } - for (const char c : gstr.toStr()) { - if (!isxdigit(c)) { - len = 0; + if (len > 0) { + return GooString(&gstr, 0, len); } } - if (len > 0) { - return GooString(&gstr, 0, len); - } } } } @@ -2532,7 +2582,7 @@ std::optional FormFieldSignature::getCheckedSignature(Goffset *checke void FormFieldSignature::print(int indent) { - printf("%*s- (%d %d): [signature] terminal: %s children: %d\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", numChildren); + printf("%*s- (%d %d): [signature] terminal: %s children: %zu\n", indent, "", ref.num, ref.gen, terminal ? "Yes" : "No", terminal ? widgets.size() : children.size()); } //------------------------------------------------------------------------ @@ -2545,11 +2595,7 @@ Form::Form(PDFDoc *docA) : doc(docA) XRef *xref = doc->getXRef(); - size = 0; - numFields = 0; - rootFields = nullptr; quadding = VariableTextQuadding::leftJustified; - defaultAppearance = nullptr; defaultResources = nullptr; Object *acroForm = doc->getCatalog()->getAcroForm(); @@ -2603,16 +2649,11 @@ Form::Form(PDFDoc *docA) : doc(docA) } alreadyReadRefs.insert(oref.getRef()); - if (numFields >= size) { - size += 16; - rootFields = (FormField **)greallocn(rootFields, size, sizeof(FormField *)); - } - std::set usedParents; - rootFields[numFields++] = createFieldFromDict(std::move(obj2), doc, oref.getRef(), nullptr, &usedParents); + rootFields.push_back(createFieldFromDict(std::move(obj2), doc, oref.getRef(), nullptr, &usedParents)); } } else { - error(errSyntaxError, -1, "Can't get Fields array\n"); + error(errSyntaxError, -1, "Can't get Fields array"); } obj1 = acroForm->dictLookup("CO"); @@ -2635,12 +2676,6 @@ Form::Form(PDFDoc *docA) : doc(docA) Form::~Form() { - int i; - for (i = 0; i < numFields; ++i) { - delete rootFields[i]; - } - gfree(rootFields); - delete defaultAppearance; delete defaultResources; } @@ -2675,24 +2710,22 @@ Object Form::fieldLookup(Dict *field, const char *key) return ::fieldLookup(field, key, &usedParents); } -FormField *Form::createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents) +std::unique_ptr Form::createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents) { - FormField *field; - const Object obj2 = Form::fieldLookup(obj.getDict(), "FT"); if (obj2.isName("Btn")) { - field = new FormFieldButton(docA, std::move(obj), aref, parent, usedParents); + return std::make_unique(docA, std::move(obj), aref, parent, usedParents); } else if (obj2.isName("Tx")) { - field = new FormFieldText(docA, std::move(obj), aref, parent, usedParents); + return std::make_unique(docA, std::move(obj), aref, parent, usedParents); } else if (obj2.isName("Ch")) { - field = new FormFieldChoice(docA, std::move(obj), aref, parent, usedParents); + return std::make_unique(docA, std::move(obj), aref, parent, usedParents); } else if (obj2.isName("Sig")) { - field = new FormFieldSignature(docA, std::move(obj), aref, parent, usedParents); + return std::make_unique(docA, std::move(obj), aref, parent, usedParents); } else { // we don't have an FT entry => non-terminal field - field = new FormField(docA, std::move(obj), aref, parent, usedParents); + return std::make_unique(docA, std::move(obj), aref, parent, usedParents); } - return field; + return {}; } static const std::string kOurDictFontNamePrefix = "popplerfont"; @@ -2703,7 +2736,7 @@ std::string Form::findFontInDefaultResources(const std::string &fontFamily, cons return {}; } - const std::string fontFamilyAndStyle = fontFamily + " " + fontStyle; + const std::string fontFamilyAndStyle = fontStyle.empty() ? fontFamily : fontFamily + " " + fontStyle; Object fontDictObj = resDict.dictLookup("Font"); assert(fontDictObj.isDict()); @@ -2711,7 +2744,7 @@ std::string Form::findFontInDefaultResources(const std::string &fontFamily, cons const Dict *fontDict = fontDictObj.getDict(); for (int i = 0; i < fontDict->getLength(); ++i) { const char *key = fontDict->getKey(i); - if (GooString::startsWith(key, kOurDictFontNamePrefix)) { + if (std::string_view(key).starts_with(kOurDictFontNamePrefix)) { const Object fontObj = fontDict->getVal(i); if (fontObj.isDict() && fontObj.dictIs("Font")) { const Object fontBaseFontObj = fontObj.dictLookup("BaseFont"); @@ -2725,12 +2758,12 @@ std::string Form::findFontInDefaultResources(const std::string &fontFamily, cons return {}; } -Form::AddFontResult Form::addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle) +Form::AddFontResult Form::addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle, bool forceName) { FamilyStyleFontSearchResult findFontRes = globalParams->findSystemFontFileForFamilyAndStyle(fontFamily, fontStyle); std::vector filesToIgnore; while (!findFontRes.filepath.empty()) { - Form::AddFontResult addFontRes = addFontToDefaultResources(findFontRes.filepath, findFontRes.faceIndex, fontFamily, fontStyle); + Form::AddFontResult addFontRes = addFontToDefaultResources(findFontRes.filepath, findFontRes.faceIndex, fontFamily, fontStyle, forceName); if (!addFontRes.fontName.empty()) { return addFontRes; } @@ -2740,9 +2773,9 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &fontFamil return {}; } -Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle) +Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle, bool forceName) { - if (!GooString::endsWith(filepath, ".ttf") && !GooString::endsWith(filepath, ".ttc") && !GooString::endsWith(filepath, ".otf")) { + if (!filepath.ends_with(".ttf") && !filepath.ends_with(".ttc") && !filepath.ends_with(".otf")) { error(errIO, -1, "We only support embedding ttf/ttc/otf fonts for now. The font file for {0:s} {1:s} was {2:s}", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str()); return {}; } @@ -2753,7 +2786,12 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, return {}; } - const std::string fontFamilyAndStyle = fontFamily + " " + fontStyle; + const std::string fontFamilyAndStyle = fontStyle.empty() ? fontFamily : fontFamily + " " + fontStyle; + + if (forceName && defaultResources && defaultResources->lookupFont(fontFamilyAndStyle.c_str())) { + error(errInternal, -1, "Form::addFontToDefaultResources: Asked to forceName but font name exists {0:s}", fontFamilyAndStyle.c_str()); + return {}; + } XRef *xref = doc->getXRef(); Object fontDict(new Dict(xref)); @@ -2775,8 +2813,8 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, { // We only support fonts with identity cmaps for now Dict *cidSystemInfo = new Dict(xref); - cidSystemInfo->set("Registry", Object(new GooString("Adobe"))); - cidSystemInfo->set("Ordering", Object(new GooString("Identity"))); + cidSystemInfo->set("Registry", Object(std::make_unique("Adobe"))); + cidSystemInfo->set("Ordering", Object(std::make_unique("Identity"))); cidSystemInfo->set("Supplement", Object(0)); descendantFont->set("CIDSystemInfo", Object(cidSystemInfo)); } @@ -2803,7 +2841,7 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, { std::unique_ptr fontDescriptor = std::make_unique(xref); fontDescriptor->set("Type", Object(objName, "FontDescriptor")); - fontDescriptor->set("FontName", Object(objName, "Noto Sans")); + fontDescriptor->set("FontName", Object(objName, fontFamilyAndStyle.c_str())); // a bit arbirary but the Flags field is mandatory... const std::string lowerCaseFontFamily = GooString::toLowerCase(fontFamily); @@ -2849,12 +2887,12 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, } if (isTrueType) { - const Ref fontFile2Ref = xref->addStreamObject(new Dict(xref), dataPtr, fileSize); + const Ref fontFile2Ref = xref->addStreamObject(new Dict(xref), dataPtr, fileSize, StreamCompression::Compress); fontDescriptor->set("FontFile2", Object(fontFile2Ref)); } else { Dict *fontFileStreamDict = new Dict(xref); fontFileStreamDict->set("Subtype", Object(objName, "OpenType")); - const Ref fontFile3Ref = xref->addStreamObject(fontFileStreamDict, dataPtr, fileSize); + const Ref fontFile3Ref = xref->addStreamObject(fontFileStreamDict, dataPtr, fileSize, StreamCompression::Compress); fontDescriptor->set("FontFile3", Object(fontFile3Ref)); } } @@ -2865,7 +2903,7 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, static const int basicMultilingualMaxCode = 65535; - const std::unique_ptr fft = FoFiTrueType::load(filepath.c_str()); + const std::unique_ptr fft = FoFiTrueType::load(filepath.c_str(), faceIndex); if (fft) { // Look for the Unicode BMP cmaps, which are 0/3 or 3/1 @@ -2878,18 +2916,38 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, return {}; } - Array *widthsInner = new Array(xref); + CIDFontsWidthsBuilder fontsWidths; + for (int code = 0; code <= basicMultilingualMaxCode; ++code) { const int glyph = fft->mapCodeToGID(unicodeBMPCMap, code); if (FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING)) { - widthsInner->add(Object(0)); + fontsWidths.addWidth(code, 0); } else { - widthsInner->add(Object(static_cast(face->glyph->metrics.horiAdvance))); + fontsWidths.addWidth(code, static_cast(face->glyph->metrics.horiAdvance)); } } Array *widths = new Array(xref); - widths->add(Object(0)); - widths->add(Object(widthsInner)); + for (const auto &segment : fontsWidths.takeSegments()) { + std::visit( + [&widths, &xref](auto &&s) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + widths->add(Object(s.first)); + auto widthsInner = std::make_unique(xref); + for (const auto &w : s.widths) { + widthsInner->add(Object(w)); + } + widths->add(Object(widthsInner.release())); + } else if constexpr (std::is_same_v) { + widths->add(Object(s.first)); + widths->add(Object(s.last)); + widths->add(Object(s.width)); + } else { + static_assert(always_false_v, "non-exhaustive visitor"); + } + }, + segment); + } descendantFont->set("W", Object(widths)); char *dataPtr = static_cast(gmalloc(2 * (basicMultilingualMaxCode + 1))); @@ -2900,7 +2958,7 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, dataPtr[i++] = (unsigned char)(glyph >> 8); dataPtr[i++] = (unsigned char)(glyph & 0xff); } - const Ref cidToGidMapStream = xref->addStreamObject(new Dict(xref), dataPtr, basicMultilingualMaxCode * 2); + const Ref cidToGidMapStream = xref->addStreamObject(new Dict(xref), dataPtr, basicMultilingualMaxCode * 2, StreamCompression::Compress); descendantFont->set("CIDToGIDMap", Object(cidToGidMapStream)); } @@ -2911,7 +2969,7 @@ Form::AddFontResult Form::addFontToDefaultResources(const std::string &filepath, const Ref fontDictRef = xref->addIndirectObject(fontDict); - std::string dictFontName = kOurDictFontNamePrefix; + std::string dictFontName = forceName ? fontFamilyAndStyle : kOurDictFontNamePrefix; Object *acroForm = doc->getCatalog()->getAcroForm(); if (resDict.isDict()) { Ref fontDictObjRef; @@ -2963,20 +3021,35 @@ std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmu std::vector Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate, GfxResources *fieldResources) { GfxResources *resources = fieldResources ? fieldResources : defaultResources; - std::shared_ptr f = resources->lookupFont(pdfFontNameToEmulate.c_str()); + std::shared_ptr f; + if (!resources) { + // There's no resources, so create one with the needed font name + addFontToDefaultResources(pdfFontNameToEmulate, "", /*forceName*/ true); + resources = defaultResources; + } + f = resources->lookupFont(pdfFontNameToEmulate.c_str()); const CharCodeToUnicode *ccToUnicode = f ? f->getToUnicode() : nullptr; if (!ccToUnicode) { - error(errInternal, -1, "Form::ensureFontsForAllCharacters: No ccToUnicode, this should not happen\n"); + error(errInternal, -1, "Form::ensureFontsForAllCharacters: No ccToUnicode, this should not happen"); return {}; // will never happen with current code } std::vector newFonts; // If the text has some characters that are not available in the font, try adding a font for those + std::unordered_set seen; for (int i = 2; i < unicodeText->getLength(); i += 2) { Unicode uChar = (unsigned char)(unicodeText->getChar(i)) << 8; uChar += (unsigned char)(unicodeText->getChar(i + 1)); + if (uChar < 128 && !std::isprint(static_cast(uChar))) { + continue; + } + if (seen.find(uChar) != seen.end()) { + continue; + } + seen.insert(uChar); + CharCode c; bool addFont = false; if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) { @@ -3021,16 +3094,16 @@ void Form::postWidgetsLoad() // every form widget here, because the AnnotWidget constructor // needs the form object that gets from the catalog. When constructing // a FormWidget the Catalog is still creating the form object - for (int i = 0; i < numFields; i++) { - rootFields[i]->fillChildrenSiblingsID(); - rootFields[i]->createWidgetAnnotations(); + for (auto &rootField : rootFields) { + rootField->fillChildrenSiblingsID(); + rootField->createWidgetAnnotations(); } } FormWidget *Form::findWidgetByRef(Ref aref) { - for (int i = 0; i < numFields; i++) { - FormWidget *result = rootFields[i]->findWidgetByRef(aref); + for (auto &rootField : rootFields) { + FormWidget *result = rootField->findWidgetByRef(aref); if (result) { return result; } @@ -3040,8 +3113,8 @@ FormWidget *Form::findWidgetByRef(Ref aref) FormField *Form::findFieldByRef(Ref aref) const { - for (int i = 0; i < numFields; i++) { - FormField *result = rootFields[i]->findFieldByRef(aref); + for (auto &rootField : rootFields) { + FormField *result = rootField->findFieldByRef(aref); if (result) { return result; } @@ -3051,8 +3124,8 @@ FormField *Form::findFieldByRef(Ref aref) const FormField *Form::findFieldByFullyQualifiedName(const std::string &name) const { - for (int i = 0; i < numFields; i++) { - FormField *result = rootFields[i]->findFieldByFullyQualifiedName(name); + for (auto &rootField : rootFields) { + FormField *result = rootField->findFieldByFullyQualifiedName(name); if (result) { return result; } @@ -3060,17 +3133,27 @@ FormField *Form::findFieldByFullyQualifiedName(const std::string &name) const return nullptr; } +FormField *Form::findFieldByFullyQualifiedNameOrRef(const std::string &field) const +{ + Ref fieldRef; + + if (field.size() > 1 && field.compare(field.size() - 2, 2, " R") == 0 && sscanf(field.c_str(), "%d %d R", &fieldRef.num, &fieldRef.gen) == 2) { + return findFieldByRef(fieldRef); + } + return findFieldByFullyQualifiedName(field); +} + void Form::reset(const std::vector &fields, bool excludeFields) { - FormField *foundField; const bool resetAllFields = fields.empty(); if (resetAllFields) { - for (int i = 0; i < numFields; i++) { - rootFields[i]->reset(std::vector()); + for (auto &rootField : rootFields) { + rootField->reset(std::vector()); } } else { if (!excludeFields) { + FormField *foundField; for (const std::string &field : fields) { Ref fieldRef; @@ -3085,26 +3168,40 @@ void Form::reset(const std::vector &fields, bool excludeFields) } } } else { - for (int i = 0; i < numFields; i++) { - rootFields[i]->reset(fields); + for (auto &rootField : rootFields) { + rootField->reset(fields); } } } } +std::string Form::findPdfFontNameToUseForSigning() +{ + static constexpr std::array fontsToUseToSign = { "Helvetica", "Arial" }; + for (const char *fontToUseToSign : fontsToUseToSign) { + std::string pdfFontName = findFontInDefaultResources(fontToUseToSign, ""); + if (!pdfFontName.empty()) { + return pdfFontName; + } + + pdfFontName = addFontToDefaultResources(fontToUseToSign, "").fontName; + if (!pdfFontName.empty()) { + return pdfFontName; + } + } + + error(errInternal, -1, "Form::findPdfFontNameToUseForSigning: No suitable font found'"); + + return {}; +} + //------------------------------------------------------------------------ // FormPageWidgets //------------------------------------------------------------------------ FormPageWidgets::FormPageWidgets(Annots *annots, unsigned int page, Form *form) { - numWidgets = 0; - widgets = nullptr; - size = 0; - if (annots && !annots->getAnnots().empty() && form) { - size = annots->getAnnots().size(); - widgets = (FormWidget **)greallocn(widgets, size, sizeof(FormWidget *)); /* For each entry in the page 'Annots' dict, try to find a matching form field */ @@ -3128,30 +3225,24 @@ FormPageWidgets::FormPageWidgets(Annots *annots, unsigned int page, Form *form) FormWidget *tmp = form->findWidgetByRef(r); if (tmp) { // We've found a corresponding form field, link it - tmp->setID(FormWidget::encodeID(page, numWidgets)); - widgets[numWidgets++] = tmp; + tmp->setID(FormWidget::encodeID(page, widgets.size())); + widgets.push_back(tmp); } } } } -void FormPageWidgets::addWidgets(const std::vector &addedWidgets, unsigned int page) +void FormPageWidgets::addWidgets(const std::vector> &addedWidgets, unsigned int page) { if (addedWidgets.empty()) { return; } - size += addedWidgets.size(); - widgets = (FormWidget **)greallocn(widgets, size, sizeof(FormWidget *)); - - for (auto frmField : addedWidgets) { + for (auto &frmField : addedWidgets) { FormWidget *frmWidget = frmField->getWidget(0); - frmWidget->setID(FormWidget::encodeID(page, numWidgets)); - widgets[numWidgets++] = frmWidget; + frmWidget->setID(FormWidget::encodeID(page, widgets.size())); + widgets.push_back(frmWidget); } } -FormPageWidgets::~FormPageWidgets() -{ - gfree(widgets); -} +FormPageWidgets::~FormPageWidgets() = default; diff --git a/poppler/Form.h b/poppler/Form.h index 248dcb2..3d9130f 100644 --- a/poppler/Form.h +++ b/poppler/Form.h @@ -6,7 +6,7 @@ // // Copyright 2006 Julien Rebetez // Copyright 2007, 2008, 2011 Carlos Garcia Campos -// Copyright 2007-2010, 2012, 2015-2022 Albert Astals Cid +// Copyright 2007-2010, 2012, 2015-2024 Albert Astals Cid // Copyright 2010 Mark Riedesel // Copyright 2011 Pino Toscano // Copyright 2012 Fabio D'Urso @@ -27,6 +27,8 @@ // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 Theofilos Intzoglou // Copyright 2022 Alexander Sulfrian +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2024 Pratham Gandhi // //======================================================================== @@ -35,14 +37,17 @@ #include "Annot.h" #include "CharTypes.h" +#include "CryptoSignBackend.h" #include "Object.h" #include "poppler_private_export.h" +#include "SignatureInfo.h" #include #include #include #include +#include class GooString; class Array; @@ -53,9 +58,10 @@ class Annots; class LinkAction; class GfxResources; class PDFDoc; -class SignatureInfo; class X509CertificateInfo; -class SignatureHandler; +namespace CryptoSign { +class VerificationInterface; +} enum FormFieldType { @@ -73,15 +79,6 @@ enum FormButtonType formButtonRadio }; -enum FormSignatureType -{ - adbe_pkcs7_sha1, - adbe_pkcs7_detached, - ETSI_CAdES_detached, - unknown_signature_type, - unsigned_signature_field -}; - enum FillValueType { fillValue, @@ -128,7 +125,7 @@ class POPPLER_PRIVATE_EXPORT FormWidget void setPartialName(const GooString &name); const GooString *getAlternateUiName() const; const GooString *getMappingName() const; - GooString *getFullyQualifiedName(); + const GooString *getFullyQualifiedName(); bool isModified() const; @@ -137,7 +134,7 @@ class POPPLER_PRIVATE_EXPORT FormWidget LinkAction *getActivationAction(); // The caller should not delete the result std::unique_ptr getAdditionalAction(Annot::FormAdditionalActionsType type); - bool setAdditionalAction(Annot::FormAdditionalActionsType t, const GooString &js); + bool setAdditionalAction(Annot::FormAdditionalActionsType t, const std::string &js); // return the unique ID corresponding to pageNum/fieldNum static int encodeID(unsigned pageNum, unsigned fieldNum); @@ -198,7 +195,7 @@ class POPPLER_PRIVATE_EXPORT FormWidgetButton : public FormWidget protected: FormFieldButton *parent() const; - GooString *onStr; + std::unique_ptr onStr; }; //------------------------------------------------------------------------ @@ -213,9 +210,9 @@ class POPPLER_PRIVATE_EXPORT FormWidgetText : public FormWidget const GooString *getContent() const; // expects a UTF16BE string - void setContent(const GooString *new_content); + void setContent(std::unique_ptr new_content); // sets the text inside the field appearance stream - void setAppearanceContent(const GooString *new_content); + void setAppearanceContent(std::unique_ptr new_content); void updateWidgetAppearance() override; @@ -252,7 +249,7 @@ class POPPLER_PRIVATE_EXPORT FormWidgetChoice : public FormWidget const GooString *getExportVal(int i) const; // select the i-th choice void select(int i); - + void setAppearanceChoiceContent(std::unique_ptr new_content); // toggle selection of the i-th choice void toggle(int i); @@ -261,7 +258,7 @@ class POPPLER_PRIVATE_EXPORT FormWidgetChoice : public FormWidget // except a UTF16BE string // only work for editable combo box, set the user-entered text as the current choice - void setEditChoice(const GooString *new_content); + void setEditChoice(std::unique_ptr new_content); const GooString *getEditChoice() const; @@ -290,11 +287,24 @@ class POPPLER_PRIVATE_EXPORT FormWidgetSignature : public FormWidget FormWidgetSignature(PDFDoc *docA, Object *dictObj, unsigned num, Ref ref, FormField *p); void updateWidgetAppearance() override; - FormSignatureType signatureType() const; - void setSignatureType(FormSignatureType fst); + CryptoSign::SignatureType signatureType() const; + void setSignatureType(CryptoSign::SignatureType fst); // Use -1 for now as validationTime - SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA); + // ocspRevocation and aiafetch might happen async in the Background + // doneCallback will be invoked once there is a result + // Note: Validation callback will likely happen from an auxillary + // thread and it is the caller of this method who is responsible + // for moving back to the main thread + // For synchronous code, don't provide validation callback + // and just call validateSignatureResult afterwards + // The returned SignatureInfo from this method does + // not have validated the certificate. + SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback); + + /// Waits, if needed, on validation callback and + /// returns a signatureinfo with validated certificates + CertificateValidationStatus validateSignatureResult(); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset @@ -307,27 +317,28 @@ class POPPLER_PRIVATE_EXPORT FormWidgetSignature : public FormWidget // field "ByteRange" in the dictionary "V". // Arguments reason and location are UTF-16 big endian strings with BOM. An empty string and nullptr are acceptable too. // Returns success. - bool signDocument(const char *filename, const char *certNickname, const char *digestName, const char *password, const GooString *reason = nullptr, const GooString *location = nullptr, const std::optional &ownerPassword = {}, - const std::optional &userPassword = {}); + std::optional signDocument(const std::string &filename, const std::string &certNickname, const std::string &password, const GooString *reason = nullptr, const GooString *location = nullptr, + const std::optional &ownerPassword = {}, const std::optional &userPassword = {}); // Same as above but adds text, font color, etc. - bool signDocumentWithAppearance(const char *filename, const char *certNickname, const char *digestName, const char *password, const GooString *reason = nullptr, const GooString *location = nullptr, - const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const GooString &signatureText = {}, const GooString &signatureTextLeft = {}, double fontSize = {}, - double leftFontSize = {}, std::unique_ptr &&fontColor = {}, double borderWidth = {}, std::unique_ptr &&borderColor = {}, std::unique_ptr &&backgroundColor = {}); + std::optional signDocumentWithAppearance(const std::string &filename, const std::string &certNickname, const std::string &password, const GooString *reason = nullptr, const GooString *location = nullptr, + const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const GooString &signatureText = {}, + const GooString &signatureTextLeft = {}, double fontSize = {}, double leftFontSize = {}, std::unique_ptr &&fontColor = {}, double borderWidth = {}, + std::unique_ptr &&borderColor = {}, std::unique_ptr &&backgroundColor = {}); // checks the length encoding of the signature and returns the hex encoded signature // if the check passed (and the checked file size as output parameter in checkedFileSize) // otherwise a nullptr is returned std::optional getCheckedSignature(Goffset *checkedFileSize); - const GooString *getSignature() const; + const std::vector &getSignature() const; private: - bool createSignature(Object &vObj, Ref vRef, const GooString &name, const GooString *signature, const GooString *reason = nullptr, const GooString *location = nullptr); + bool createSignature(Object &vObj, Ref vRef, const GooString &name, int placeholderLength, const GooString *reason, const GooString *location, CryptoSign::SignatureType signatureType); bool getObjectStartEnd(const GooString &filename, int objNum, Goffset *objStart, Goffset *objEnd, const std::optional &ownerPassword, const std::optional &userPassword); bool updateOffsets(FILE *f, Goffset objStart, Goffset objEnd, Goffset *sigStart, Goffset *sigEnd, Goffset *fileSize); - bool updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString *signature); + bool updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const std::vector &signature); }; //------------------------------------------------------------------------ @@ -354,23 +365,23 @@ class POPPLER_PRIVATE_EXPORT FormField void setStandAlone(bool value) { standAlone = value; } bool isStandAlone() const { return standAlone; } - GooString *getDefaultAppearance() const { return defaultAppearance; } + GooString *getDefaultAppearance() const { return defaultAppearance.get(); } void setDefaultAppearance(const std::string &appearance); bool hasTextQuadding() const { return hasQuadding; } VariableTextQuadding getTextQuadding() const { return quadding; } - const GooString *getPartialName() const { return partialName; } + const GooString *getPartialName() const { return partialName.get(); } void setPartialName(const GooString &name); - const GooString *getAlternateUiName() const { return alternateUiName; } - const GooString *getMappingName() const { return mappingName; } - GooString *getFullyQualifiedName(); + const GooString *getAlternateUiName() const { return alternateUiName.get(); } + const GooString *getMappingName() const { return mappingName.get(); } + const GooString *getFullyQualifiedName() const; FormWidget *findWidgetByRef(Ref aref); - int getNumWidgets() const { return terminal ? numChildren : 0; } - FormWidget *getWidget(int i) const { return terminal ? widgets[i] : nullptr; } - int getNumChildren() const { return !terminal ? numChildren : 0; } - FormField *getChildren(int i) const { return children[i]; } + int getNumWidgets() const { return terminal ? widgets.size() : 0; } + FormWidget *getWidget(int i) const { return terminal ? widgets[i].get() : nullptr; } + int getNumChildren() const { return !terminal ? children.size() : 0; } + FormField *getChildren(int i) const { return !terminal ? children[i].get() : nullptr; } // only implemented in FormFieldButton virtual void fillChildrenSiblingsID(); @@ -384,6 +395,8 @@ class POPPLER_PRIVATE_EXPORT FormField FormField *findFieldByRef(Ref aref); FormField *findFieldByFullyQualifiedName(const std::string &name); + bool getNoExport() const { return noExport; } + protected: void _createWidget(Object *obj, Ref aref); void createChildren(std::set *usedParents); @@ -396,19 +409,19 @@ class POPPLER_PRIVATE_EXPORT FormField Object obj; PDFDoc *doc; XRef *xref; - FormField **children; + std::vector> children; FormField *parent; - int numChildren; - FormWidget **widgets; + std::vector> widgets; bool readOnly; + bool noExport; - GooString *partialName; // T field - GooString *alternateUiName; // TU field - GooString *mappingName; // TM field - GooString *fullyQualifiedName; + std::unique_ptr partialName; // T field + std::unique_ptr alternateUiName; // TU field + std::unique_ptr mappingName; // TM field + mutable std::unique_ptr fullyQualifiedName; // Variable Text - GooString *defaultAppearance; + std::unique_ptr defaultAppearance; bool hasQuadding; VariableTextQuadding quadding; @@ -416,7 +429,7 @@ class POPPLER_PRIVATE_EXPORT FormField bool standAlone; private: - FormField() { } + FormField() = default; }; //------------------------------------------------------------------------ @@ -446,7 +459,7 @@ class FormFieldButton : public FormField // For radio buttons, return the fields of the other radio buttons in the same group FormFieldButton *getSibling(int i) const { return siblings[i]; } - int getNumSiblings() const { return numSiblings; } + int getNumSiblings() const { return int(siblings.size()); } void print(int indent) override; void reset(const std::vector &excludedFields) override; @@ -456,10 +469,8 @@ class FormFieldButton : public FormField protected: void updateState(const char *state); - FormFieldButton **siblings; // IDs of dependent buttons (each button of a radio field has all the others buttons - // of the same field in this array) - int numSiblings; - + std::vector siblings; // IDs of dependent buttons (each button of a radio field has all the others buttons + // of the same field in this array) FormButtonType btype; int size; int active_child; // only used for combo box @@ -477,10 +488,10 @@ class FormFieldText : public FormField public: FormFieldText(PDFDoc *docA, Object &&dictObj, const Ref ref, FormField *parent, std::set *usedParents); - const GooString *getContent() const { return content; } - const GooString *getAppearanceContent() const { return internalContent ? internalContent : content; } - void setContentCopy(const GooString *new_content); - void setAppearanceContentCopy(const GooString *new_content); + const GooString *getContent() const { return content.get(); } + const GooString *getAppearanceContent() const { return internalContent ? internalContent.get() : content.get(); } + void setContent(std::unique_ptr new_content); + void setAppearanceContent(std::unique_ptr new_content); ~FormFieldText() override; bool isMultiline() const { return multiline; } @@ -507,9 +518,9 @@ class FormFieldText : public FormField int parseDA(std::vector *daToks); void fillContent(FillValueType fillType); - GooString *content; - GooString *internalContent; - GooString *defaultContent; + std::unique_ptr content; + std::unique_ptr internalContent; + std::unique_ptr defaultContent; bool multiline; bool password; bool fileSelect; @@ -532,10 +543,13 @@ class FormFieldChoice : public FormField ~FormFieldChoice() override; int getNumChoices() const { return numChoices; } - const GooString *getChoice(int i) const { return choices ? choices[i].optionName : nullptr; } - const GooString *getExportVal(int i) const { return choices ? choices[i].exportVal : nullptr; } + const GooString *getChoice(int i) const { return choices ? choices[i].optionName.get() : nullptr; } + const GooString *getExportVal(int i) const { return choices ? choices[i].exportVal.get() : nullptr; } // For multi-select choices it returns the first one const GooString *getSelectedChoice() const; + const GooString *getAppearanceSelectedChoice() const { return appearanceSelectedChoice ? appearanceSelectedChoice.get() : getSelectedChoice(); } + + void setAppearanceChoiceContent(std::unique_ptr new_content); // select the i-th choice void select(int i); @@ -547,7 +561,7 @@ class FormFieldChoice : public FormField void deselectAll(); // only work for editable combo box, set the user-entered text as the current choice - void setEditChoice(const GooString *new_content); + void setEditChoice(std::unique_ptr new_content); const GooString *getEditChoice() const; @@ -580,15 +594,16 @@ class FormFieldChoice : public FormField struct ChoiceOpt { - GooString *exportVal; // the export value ("internal" name) - GooString *optionName; // displayed name - bool selected; // if this choice is selected + std::unique_ptr exportVal; // the export value ("internal" name) + std::unique_ptr optionName; // displayed name + bool selected = false; // if this choice is selected }; int numChoices; ChoiceOpt *choices; bool *defaultChoices; - GooString *editedChoice; + std::unique_ptr editedChoice; + std::unique_ptr appearanceSelectedChoice; int topIdx; // TI }; @@ -602,7 +617,9 @@ class POPPLER_PRIVATE_EXPORT FormFieldSignature : public FormField FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set *usedParents); // Use -1 for now as validationTime - SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA); + SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function &doneCallback); + + CertificateValidationStatus validateSignatureResult(); // returns a list with the boundaries of the signed ranges // the elements of the list are of type Goffset @@ -615,10 +632,10 @@ class POPPLER_PRIVATE_EXPORT FormFieldSignature : public FormField ~FormFieldSignature() override; Object *getByteRange() { return &byte_range; } - const GooString *getSignature() const { return signature; } - void setSignature(const GooString &sig); - FormSignatureType getSignatureType() const { return signature_type; } - void setSignatureType(FormSignatureType t) { signature_type = t; } + const std::vector &getSignature() const { return signature; } + void setSignature(std::vector &&sig); + CryptoSign::SignatureType getSignatureType() const { return signature_type; } + void setSignatureType(CryptoSign::SignatureType t) { signature_type = t; } const GooString &getCustomAppearanceContent() const; void setCustomAppearanceContent(const GooString &s); @@ -639,17 +656,18 @@ class POPPLER_PRIVATE_EXPORT FormFieldSignature : public FormField private: void parseInfo(); - void hashSignedDataBlock(SignatureHandler *handler, Goffset block_len); + void hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len); - FormSignatureType signature_type; + CryptoSign::SignatureType signature_type; Object byte_range; - GooString *signature; + std::vector signature; SignatureInfo *signature_info; GooString customAppearanceContent; GooString customAppearanceLeftContent; double customAppearanceLeftFontSize = 20; Ref imageResource = Ref::INVALID(); std::unique_ptr certificate_info; + std::unique_ptr signature_handler; void print(int indent) override; }; @@ -676,12 +694,16 @@ class POPPLER_PRIVATE_EXPORT Form /* Creates a new Field of the type specified in obj's dict. used in Form::Form , FormField::FormField and Page::loadStandaloneFields */ - static FormField *createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents); + static std::unique_ptr createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set *usedParents); // Finds in the default resources dictionary a font named popplerfontXXX that // has the given fontFamily and fontStyle. This makes us relatively sure that we added that font ourselves std::string findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const; + // Finds in the default resources a font that is suitable to create a signature annotation. + // If none is found then it is added to the default resources. + std::string findPdfFontNameToUseForSigning(); + struct AddFontResult { std::string fontName; @@ -689,8 +711,9 @@ class POPPLER_PRIVATE_EXPORT Form }; // Finds in the system a font name matching the given fontFamily and fontStyle - // And adds it to the default resources dictionary, font name there will be popplerfontXXX - AddFontResult addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle); + // And adds it to the default resources dictionary, font name there will be popplerfontXXX except if forceName is true, + // in that case the font name will be fontFamily + " " + fontStyle (if fontStyle is empty just fontFamily) + AddFontResult addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle, bool forceName = false); // Finds in the default resources dictionary a font named popplerfontXXX that // emulates fontToEmulate and can draw the given char @@ -703,9 +726,9 @@ class POPPLER_PRIVATE_EXPORT Form std::vector ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate, GfxResources *fieldResources = nullptr); bool getNeedAppearances() const { return needAppearances; } - int getNumFields() const { return numFields; } - FormField *getRootField(int i) const { return rootFields[i]; } - const GooString *getDefaultAppearance() const { return defaultAppearance; } + int getNumFields() const { return rootFields.size(); } + FormField *getRootField(int i) const { return rootFields[i].get(); } + const GooString *getDefaultAppearance() const { return defaultAppearance.get(); } VariableTextQuadding getTextQuadding() const { return quadding; } GfxResources *getDefaultResources() const { return defaultResources; } Object *getDefaultResourcesObj() { return &resDict; } @@ -713,6 +736,7 @@ class POPPLER_PRIVATE_EXPORT Form FormWidget *findWidgetByRef(Ref aref); FormField *findFieldByRef(Ref aref) const; FormField *findFieldByFullyQualifiedName(const std::string &name) const; + FormField *findFieldByFullyQualifiedNameOrRef(const std::string &field) const; void postWidgetsLoad(); @@ -722,14 +746,13 @@ class POPPLER_PRIVATE_EXPORT Form private: // Finds in the system a font name matching the given fontFamily and fontStyle - // And adds it to the default resources dictionary, font name there will be popplerfontXXX - AddFontResult addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle); + // And adds it to the default resources dictionary, font name there will be popplerfontXXX except if forceName is true, + // in that case the font name will be fontFamily + " " + fontStyle (if fontStyle is empty just fontFamily) + AddFontResult addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle, bool forceName = false); AddFontResult doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate); - FormField **rootFields; - int numFields; - int size; + std::vector> rootFields; PDFDoc *const doc; bool needAppearances; GfxResources *defaultResources; @@ -737,7 +760,7 @@ class POPPLER_PRIVATE_EXPORT Form std::vector calculateOrder; // Variable Text - GooString *defaultAppearance; + std::unique_ptr defaultAppearance; VariableTextQuadding quadding; }; @@ -754,14 +777,12 @@ class POPPLER_PRIVATE_EXPORT FormPageWidgets FormPageWidgets(const FormPageWidgets &) = delete; FormPageWidgets &operator=(const FormPageWidgets &) = delete; - int getNumWidgets() const { return numWidgets; } + int getNumWidgets() const { return widgets.size(); } FormWidget *getWidget(int i) const { return widgets[i]; } - void addWidgets(const std::vector &addedWidgets, unsigned int page); + void addWidgets(const std::vector> &addedWidgets, unsigned int page); private: - FormWidget **widgets; - int numWidgets; - int size; + std::vector widgets; }; #endif diff --git a/poppler/Function.cc b/poppler/Function.cc index 043ae8e..d84c4e3 100644 --- a/poppler/Function.cc +++ b/poppler/Function.cc @@ -13,13 +13,14 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2006, 2008-2010, 2013-2015, 2017-2020, 2022 Albert Astals Cid +// Copyright (C) 2006, 2008-2010, 2013-2015, 2017-2020, 2022-2024 Albert Astals Cid // Copyright (C) 2006 Jeff Muizelaar // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012 Adam Reichold // Copyright (C) 2013 Fabio D'Urso +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -50,7 +51,7 @@ Function::Function() : domain {} { } -Function::~Function() { } +Function::~Function() = default; Function *Function::parse(Object *funcObj) { @@ -193,7 +194,7 @@ IdentityFunction::IdentityFunction() hasRange = false; } -IdentityFunction::~IdentityFunction() { } +IdentityFunction::~IdentityFunction() = default; void IdentityFunction::transform(const double *in, double *out) const { @@ -363,10 +364,13 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) : cacheOut {} error(errSyntaxError, -1, "Function has invalid number of samples"); return; } + if (!str->reset()) { + error(errSyntaxError, -1, "Stream reset error"); + return; + } buf = 0; bits = 0; bitMask = (1 << sampleBits) - 1; - str->reset(); for (i = 0; i < nSamples; ++i) { if (sampleBits == 8) { s = str->getChar(); @@ -524,7 +528,7 @@ void SampledFunction::transform(const double *in, double *out) const bool SampledFunction::hasDifferentResultSet(const Function *func) const { - if (func->getType() == 0) { + if (func->getType() == Type::Sampled) { SampledFunction *compTo = (SampledFunction *)func; if (compTo->getSampleNumber() != nSamples) { return true; @@ -622,7 +626,7 @@ ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) ok = true; } -ExponentialFunction::~ExponentialFunction() { } +ExponentialFunction::~ExponentialFunction() = default; ExponentialFunction::ExponentialFunction(const ExponentialFunction *func) : Function(func) { @@ -656,7 +660,6 @@ void ExponentialFunction::transform(const double *in, double *out) const } } } - return; } //------------------------------------------------------------------------ @@ -762,7 +765,6 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, std::set n = funcs[0]->getOutputSize(); ok = true; - return; } StitchingFunction::StitchingFunction(const StitchingFunction *func) : Function(func) @@ -797,7 +799,7 @@ StitchingFunction::~StitchingFunction() } } } - gfree(funcs); + gfree(static_cast(funcs)); gfree(bounds); gfree(encode); gfree(scale); @@ -1108,7 +1110,6 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) int i; code = nullptr; - codeString = nullptr; codeSize = 0; ok = false; @@ -1127,11 +1128,14 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) goto err1; } str = funcObj->getStream(); + if (!str->reset()) { + error(errSyntaxError, -1, "Stream reset error"); + goto err1; + } //----- parse the function - codeString = new GooString(); - str->reset(); - if (getToken(str).cmp("{") != 0) { + codeString = std::make_unique(); + if (getToken(str)->cmp("{") != 0) { error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); goto err1; } @@ -1174,7 +1178,6 @@ PostScriptFunction::PostScriptFunction(const PostScriptFunction *func) : Functio PostScriptFunction::~PostScriptFunction() { gfree(code); - delete codeString; } void PostScriptFunction::transform(const double *in, double *out) const @@ -1231,8 +1234,10 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) int a, b, mid, cmp; while (true) { - GooString tok = getToken(str); - const char *p = tok.c_str(); + // This needs to be on the heap to help make parseCode + // able to call itself more times recursively + std::unique_ptr tok = getToken(str); + const char *p = tok->c_str(); if (isdigit(*p) || *p == '.' || *p == '-') { isReal = false; for (; *p; ++p) { @@ -1244,13 +1249,13 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) resizeCode(*codePtr); if (isReal) { code[*codePtr].type = psReal; - code[*codePtr].real = gatof(tok.c_str()); + code[*codePtr].real = gatof(tok->c_str()); } else { code[*codePtr].type = psInt; - code[*codePtr].intg = atoi(tok.c_str()); + code[*codePtr].intg = atoi(tok->c_str()); } ++*codePtr; - } else if (!tok.cmp("{")) { + } else if (!tok->cmp("{")) { opPtr = *codePtr; *codePtr += 3; resizeCode(opPtr + 2); @@ -1258,7 +1263,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) return false; } tok = getToken(str); - if (!tok.cmp("{")) { + if (!tok->cmp("{")) { elsePtr = *codePtr; if (!parseCode(str, codePtr)) { return false; @@ -1267,7 +1272,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) } else { elsePtr = -1; } - if (!tok.cmp("if")) { + if (!tok->cmp("if")) { if (elsePtr >= 0) { error(errSyntaxError, -1, "Got 'if' operator with two blocks in PostScript function"); return false; @@ -1276,7 +1281,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) code[opPtr].op = psOpIf; code[opPtr + 2].type = psBlock; code[opPtr + 2].blk = *codePtr; - } else if (!tok.cmp("ifelse")) { + } else if (!tok->cmp("ifelse")) { if (elsePtr < 0) { error(errSyntaxError, -1, "Got 'ifelse' operator with one block in PostScript function"); return false; @@ -1291,7 +1296,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) error(errSyntaxError, -1, "Expected if/ifelse operator in PostScript function"); return false; } - } else if (!tok.cmp("}")) { + } else if (!tok->cmp("}")) { resizeCode(*codePtr); code[*codePtr].type = psOperator; code[*codePtr].op = psOpReturn; @@ -1304,7 +1309,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) // invariant: psOpNames[a] < tok < psOpNames[b] while (b - a > 1) { mid = (a + b) / 2; - cmp = tok.cmp(psOpNames[mid]); + cmp = tok->cmp(psOpNames[mid]); if (cmp > 0) { a = mid; } else if (cmp < 0) { @@ -1314,7 +1319,7 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) } } if (cmp != 0) { - error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", &tok); + error(errSyntaxError, -1, "Unknown operator '{0:t}' in PostScript function", tok.get()); return false; } resizeCode(*codePtr); @@ -1326,12 +1331,12 @@ bool PostScriptFunction::parseCode(Stream *str, int *codePtr) return true; } -GooString PostScriptFunction::getToken(Stream *str) +std::unique_ptr PostScriptFunction::getToken(Stream *str) { int c; bool comment; - GooString s; + std::string s; comment = false; while (true) { if ((c = str->getChar()) == EOF) { @@ -1349,10 +1354,10 @@ GooString PostScriptFunction::getToken(Stream *str) } } if (c == '{' || c == '}') { - s.append((char)c); + s.push_back((char)c); } else if (isdigit(c) || c == '.' || c == '-') { while (true) { - s.append((char)c); + s.push_back((char)c); c = str->lookChar(); if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { break; @@ -1362,7 +1367,7 @@ GooString PostScriptFunction::getToken(Stream *str) } } else { while (true) { - s.append((char)c); + s.push_back((char)c); c = str->lookChar(); if (c == EOF || !isalnum(c)) { break; @@ -1371,7 +1376,7 @@ GooString PostScriptFunction::getToken(Stream *str) codeString->append(c); } } - return s; + return std::make_unique(s); } void PostScriptFunction::resizeCode(int newSize) diff --git a/poppler/Function.h b/poppler/Function.h index c1544f4..cecc762 100644 --- a/poppler/Function.h +++ b/poppler/Function.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2009, 2010, 2018, 2019, 2021 Albert Astals Cid +// Copyright (C) 2009, 2010, 2018, 2019, 2021, 2024 Albert Astals Cid // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2012 Thomas Freitag @@ -61,13 +61,16 @@ class POPPLER_PRIVATE_EXPORT Function virtual Function *copy() const = 0; - // Return the function type: - // -1 : identity - // 0 : sampled - // 2 : exponential - // 3 : stitching - // 4 : PostScript - virtual int getType() const = 0; + enum class Type + { + Identity, + Sampled, + Exponential, + Stitching, + PostScript + }; + + virtual Type getType() const = 0; // Return size of input and output tuples. int getInputSize() const { return m; } @@ -108,7 +111,7 @@ class IdentityFunction : public Function IdentityFunction(); ~IdentityFunction() override; Function *copy() const override { return new IdentityFunction(); } - int getType() const override { return -1; } + Type getType() const override { return Type::Identity; } void transform(const double *in, double *out) const override; bool isOk() const override { return true; } @@ -125,7 +128,7 @@ class SampledFunction : public Function SampledFunction(Object *funcObj, Dict *dict); ~SampledFunction() override; Function *copy() const override { return new SampledFunction(this); } - int getType() const override { return 0; } + Type getType() const override { return Type::Sampled; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } bool hasDifferentResultSet(const Function *func) const override; @@ -168,7 +171,7 @@ class ExponentialFunction : public Function ExponentialFunction(Object *funcObj, Dict *dict); ~ExponentialFunction() override; Function *copy() const override { return new ExponentialFunction(this); } - int getType() const override { return 2; } + Type getType() const override { return Type::Exponential; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } @@ -196,7 +199,7 @@ class StitchingFunction : public Function StitchingFunction(Object *funcObj, Dict *dict, std::set *usedParents); ~StitchingFunction() override; Function *copy() const override { return new StitchingFunction(this); } - int getType() const override { return 3; } + Type getType() const override { return Type::Stitching; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } @@ -227,20 +230,20 @@ class PostScriptFunction : public Function PostScriptFunction(Object *funcObj, Dict *dict); ~PostScriptFunction() override; Function *copy() const override { return new PostScriptFunction(this); } - int getType() const override { return 4; } + Type getType() const override { return Type::PostScript; } void transform(const double *in, double *out) const override; bool isOk() const override { return ok; } - const GooString *getCodeString() const { return codeString; } + const GooString *getCodeString() const { return codeString.get(); } private: explicit PostScriptFunction(const PostScriptFunction *func); bool parseCode(Stream *str, int *codePtr); - GooString getToken(Stream *str); + std::unique_ptr getToken(Stream *str); void resizeCode(int newSize); void exec(PSStack *stack, int codePtr) const; - GooString *codeString; + std::unique_ptr codeString; PSObject *code; int codeSize; mutable double cacheIn[funcMaxInputs]; diff --git a/poppler/GPGMECryptoSignBackend.cc b/poppler/GPGMECryptoSignBackend.cc new file mode 100644 index 0000000..5ce08d3 --- /dev/null +++ b/poppler/GPGMECryptoSignBackend.cc @@ -0,0 +1,575 @@ +//======================================================================== +// +// GPGMECryptoSignBackend.cc +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#include "CryptoSignBackend.h" +#include "config.h" +#include "GPGMECryptoSignBackend.h" +#include "DistinguishedNameParser.h" +#include +#include +#include +#include +#include +#include +#if DUMP_SIGNATURE_DATA +# include +#endif + +bool GpgSignatureBackend::hasSufficientVersion() +{ + // gpg 2.4.0 does not support padded signatures. + // Most gpg signatures are padded. This is fixed for 2.4.1 + // gpg 2.4.0 does not support generating signatures + // with definite lengths. This is also fixed for 2.4.1. + // this has also been fixed in 2.2.42 in the 2.2 branch + auto version = GpgME::engineInfo(GpgME::GpgSMEngine).engineVersion(); + if (version > "2.4.0") { + return true; + } + if (version >= "2.3.0") { // development branch for 2.4 releases; no more releases here + return false; + } + return version >= "2.2.42"; +} + +/// GPGME helper methods + +// gpgme++'s string-like functions returns char pointers that can be nullptr +// Creating std::string from nullptr is, depending on c++ standards versions +// either undefined behavior or illegal, so we need a helper. + +static std::string fromCharPtr(const char *data) +{ + if (data) { + return std::string { data }; + } + return {}; +} + +static bool isSuccess(const GpgME::Error &err) +{ + if (err) { + return false; + } + if (err.isCanceled()) { + return false; + } + return true; +} + +template +static bool isValidResult(const Result &result) +{ + return isSuccess(result.error()); +} + +template +static bool hasValidResult(const std::optional &result) +{ + if (!result) { + return false; + } + return isValidResult(result.value()); +} + +static std::optional getSignature(const GpgME::VerificationResult &result, size_t signatureNumber) +{ + if (result.numSignatures() > signatureNumber) { + return result.signature(signatureNumber); + } + return std::nullopt; +} + +static X509CertificateInfo::Validity getValidityFromSubkey(const GpgME::Subkey &key) +{ + X509CertificateInfo::Validity validity; + validity.notBefore = key.creationTime(); + if (key.neverExpires()) { + validity.notAfter = std::numeric_limits::max(); + } else { + validity.notAfter = key.expirationTime(); + } + return validity; +} + +static X509CertificateInfo::EntityInfo getEntityInfoFromKey(std::string_view dnString) +{ + const auto dn = DN::parseString(dnString); + X509CertificateInfo::EntityInfo info; + info.commonName = DN::FindFirstValue(dn, "CN").value_or(std::string {}); + info.organization = DN::FindFirstValue(dn, "O").value_or(std::string {}); + info.email = DN::FindFirstValue(dn, "EMAIL").value_or(std::string {}); + info.distinguishedName = std::string { dnString }; + return info; +} + +static std::unique_ptr getCertificateInfoFromKey(const GpgME::Key &key, GpgME::Protocol protocol) +{ + auto certificateInfo = std::make_unique(); + if (protocol == GpgME::CMS) { + certificateInfo->setIssuerInfo(getEntityInfoFromKey(fromCharPtr(key.issuerName()))); + auto subjectInfo = getEntityInfoFromKey(fromCharPtr(key.userID(0).id())); + if (subjectInfo.email.empty()) { + subjectInfo.email = fromCharPtr(key.userID(1).email()); + } + certificateInfo->setSubjectInfo(std::move(subjectInfo)); + } else if (protocol == GpgME::OpenPGP) { + X509CertificateInfo::EntityInfo info; + info.email = fromCharPtr(key.userID(0).email()); + info.commonName = fromCharPtr(key.userID(0).name()); + info.distinguishedName = fromCharPtr(key.userID(0).id()); + certificateInfo->setSubjectInfo(std::move(info)); + certificateInfo->setCertificateType(CertificateType::PGP); + } + certificateInfo->setValidity(getValidityFromSubkey(key.subkey(0))); + certificateInfo->setSerialNumber(GooString { DN::detail::parseHexString(fromCharPtr(key.issuerSerial())).value_or("") }); + certificateInfo->setNickName(GooString(fromCharPtr(key.primaryFingerprint()))); + X509CertificateInfo::PublicKeyInfo pkInfo; + pkInfo.publicKeyStrength = key.subkey(0).length(); + switch (key.subkey(0).publicKeyAlgorithm()) { + case GpgME::Subkey::AlgoDSA: + pkInfo.publicKeyType = DSAKEY; + break; + case GpgME::Subkey::AlgoECC: + case GpgME::Subkey::AlgoECDH: + case GpgME::Subkey::AlgoECDSA: + case GpgME::Subkey::AlgoEDDSA: + pkInfo.publicKeyType = ECKEY; + break; + case GpgME::Subkey::AlgoRSA: + case GpgME::Subkey::AlgoRSA_E: + case GpgME::Subkey::AlgoRSA_S: + pkInfo.publicKeyType = RSAKEY; + break; + case GpgME::Subkey::AlgoELG: + case GpgME::Subkey::AlgoELG_E: + case GpgME::Subkey::AlgoMax: + case GpgME::Subkey::AlgoUnknown: +#if GPGMEPP_VERSION > ((1 << 16) | (24 << 8) | (0)) + case GpgME::Subkey::AlgoKyber: +#endif + pkInfo.publicKeyType = OTHERKEY; + } + { + auto ctx = GpgME::Context::create(protocol); + GpgME::Data pubkeydata; + const auto err = ctx->exportPublicKeys(key.primaryFingerprint(), pubkeydata); + if (isSuccess(err)) { + certificateInfo->setCertificateDER(GooString(pubkeydata.toString())); + } + } + + certificateInfo->setPublicKeyInfo(std::move(pkInfo)); + + int kue = 0; + // this block is kind of a hack. GPGSM collapses multiple + // into one bit, so trying to match it back can never be good + if (key.canSign()) { + kue |= KU_NON_REPUDIATION; + kue |= KU_DIGITAL_SIGNATURE; + } + if (key.canEncrypt()) { + kue |= KU_KEY_ENCIPHERMENT; + kue |= KU_DATA_ENCIPHERMENT; + } + if (key.canCertify()) { + kue |= KU_KEY_CERT_SIGN; + } + certificateInfo->setKeyUsageExtensions(kue); + + auto subkey = key.subkey(0); + if (subkey.isCardKey()) { + certificateInfo->setKeyLocation(KeyLocation::HardwareToken); + } else if (subkey.isSecret()) { + certificateInfo->setKeyLocation(KeyLocation::Computer); + } + + certificateInfo->setQualified(subkey.isQualified()); + + return certificateInfo; +} + +/// implementation of header file + +GpgSignatureBackend::GpgSignatureBackend() +{ + GpgME::initializeLibrary(); +} + +std::unique_ptr GpgSignatureBackend::createSigningHandler(const std::string &certID, HashAlgorithm /*digestAlgTag*/) +{ + return std::make_unique(certID); +} + +std::unique_ptr GpgSignatureBackend::createVerificationHandler(std::vector &&pkcs7, CryptoSign::SignatureType type) +{ + switch (type) { + case CryptoSign::SignatureType::unknown_signature_type: + case CryptoSign::SignatureType::unsigned_signature_field: + return {}; + case CryptoSign::SignatureType::ETSI_CAdES_detached: + case CryptoSign::SignatureType::adbe_pkcs7_detached: + case CryptoSign::SignatureType::adbe_pkcs7_sha1: + return std::make_unique(std::move(pkcs7), GpgME::CMS); + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + return std::make_unique(std::move(pkcs7), GpgME::OpenPGP); + } + return {}; +} + +std::vector> GpgSignatureBackend::getAvailableSigningCertificates() +{ + std::vector> certificates; + for (auto type : GpgSignatureConfiguration::allowedTypes()) { + const auto context = GpgME::Context::create(type); + auto err = context->startKeyListing(static_cast(nullptr), true /*secretOnly*/); + while (isSuccess(err)) { + const auto key = context->nextKey(err); + if (!key.isNull() && isSuccess(err)) { + if (key.isBad()) { + continue; + } + if (!key.canSign()) { + continue; + } + certificates.push_back(getCertificateInfoFromKey(key, type)); + } else { + break; + } + } + } + return certificates; +} + +GpgSignatureCreation::GpgSignatureCreation(const std::string &certId) +{ + for (auto type : GpgSignatureConfiguration::allowedTypes()) { + GpgME::Error error; + auto context = GpgME::Context::create(type); + const auto signingKey = context->key(certId.c_str(), error, true); + if (isSuccess(error)) { + context->addSigningKey(signingKey); + key = signingKey; + gpgContext = std::move(context); + protocol = type; + break; + } + } +} + +void GpgSignatureCreation::addData(unsigned char *dataBlock, int dataLen) +{ + gpgData.write(dataBlock, dataLen); +} +std::variant, CryptoSign::SigningError> GpgSignatureCreation::signDetached(const std::string &password) +{ + if (!key) { + return CryptoSign::SigningError::KeyMissing; + } + gpgData.rewind(); + GpgME::Data signatureData; + const auto signingResult = gpgContext->sign(gpgData, signatureData, GpgME::SignatureMode::Detached); + if (!isValidResult(signingResult)) { + if (signingResult.error().isCanceled()) { + return CryptoSign::SigningError::UserCancelled; + } else { + switch (signingResult.error().code()) { + case GPG_ERR_NO_PASSPHRASE: // this is likely the user pressing enter. Let's treat it as cancelled for now + return CryptoSign::SigningError::UserCancelled; + case GPG_ERR_BAD_PASSPHRASE: + return CryptoSign::SigningError::BadPassphrase; + } + return CryptoSign::SigningError::GenericError; + } + } + + auto signatureString = signatureData.toString(); + if (protocol == GpgME::OpenPGP) { + // We need to prepare for padding PGP doesn't like padding as such + // so we disguise it as a comment packet. + // the size of the comment packet prefix and size is the first 6 bytes. + static const int prefixandsize = 6; + std::string prefix { std::initializer_list { + (char)0xfd, + (char)0xff, + } }; + + if (CryptoSign::maxSupportedSignatureSize - prefixandsize <= signatureString.size()) { + return CryptoSign::SigningError::InternalError; + } + std::array bytes; + int n = CryptoSign::maxSupportedSignatureSize - prefixandsize - signatureString.size(); + + bytes[0] = (n >> 24) & 0xFF; + bytes[1] = (n >> 16) & 0xFF; + bytes[2] = (n >> 8) & 0xFF; + bytes[3] = n & 0xFF; + + prefix.append(std::begin(bytes), std::end(bytes)); + signatureString.append(prefix); + } + return std::vector(signatureString.begin(), signatureString.end()); +} +CryptoSign::SignatureType GpgSignatureCreation::signatureType() const +{ + if (protocol == GpgME::CMS) { + return CryptoSign::SignatureType::adbe_pkcs7_detached; + } else if (protocol == GpgME::OpenPGP) { + return CryptoSign::SignatureType::g10c_pgp_signature_detached; + } + return CryptoSign::SignatureType::unknown_signature_type; +} + +std::unique_ptr GpgSignatureCreation::getCertificateInfo() const +{ + if (!key) { + return nullptr; + } + return getCertificateInfoFromKey(*key, protocol); +} + +GpgSignatureVerification::GpgSignatureVerification(const std::vector &p7data, GpgME::Protocol protocol_) + : gpgContext { GpgME::Context::create(protocol_) }, signatureData(reinterpret_cast(p7data.data()), p7data.size()), protocol { protocol_ } +{ + gpgContext->setOffline(true); + signatureData.setEncoding(GpgME::Data::BinaryEncoding); +#if DUMP_SIGNATURE_DATA + static int debugFileCounter = 0; + debugFileCounter++; + std::ofstream debugSignatureData("/tmp/popplerstuff/signatureData" + std::to_string(debugFileCounter) + ".sig", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); + debugSignedData = std::make_unique("/tmp/popplerstuff/signedData" + std::to_string(debugFileCounter) + ".data", std::ofstream::out | std::ofstream::trunc | std::ofstream::binary); + debugSignatureData.write(reinterpret_cast(p7data.data()), p7data.size()); +#endif +} + +void GpgSignatureVerification::addData(unsigned char *dataBlock, int dataLen) +{ + signedData.write(dataBlock, dataLen); +#if DUMP_SIGNATURE_DATA + debugSignedData->write(reinterpret_cast(dataBlock), dataLen); +#endif +} + +std::unique_ptr GpgSignatureVerification::getCertificateInfo() const +{ + if (!hasValidResult(gpgResult)) { + return nullptr; + } + auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + return nullptr; + } + auto gpgInfo = getCertificateInfoFromKey(signature->key(true, false), protocol); + return gpgInfo; +} + +HashAlgorithm GpgSignatureVerification::getHashAlgorithm() const +{ + if (gpgResult) { + const auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + return HashAlgorithm::Unknown; + } + switch (signature->hashAlgorithm()) { + case GPGME_MD_MD5: + return HashAlgorithm::Md5; + case GPGME_MD_SHA1: + return HashAlgorithm::Sha1; + case GPGME_MD_MD2: + return HashAlgorithm::Md2; + case GPGME_MD_SHA256: + return HashAlgorithm::Sha256; + case GPGME_MD_SHA384: + return HashAlgorithm::Sha384; + case GPGME_MD_SHA512: + return HashAlgorithm::Sha512; + case GPGME_MD_SHA224: + return HashAlgorithm::Sha224; + case GPGME_MD_NONE: + case GPGME_MD_RMD160: + case GPGME_MD_TIGER: + case GPGME_MD_HAVAL: + case GPGME_MD_MD4: + case GPGME_MD_CRC32: + case GPGME_MD_CRC32_RFC1510: + case GPGME_MD_CRC24_RFC2440: + default: + return HashAlgorithm::Unknown; + } + } + return HashAlgorithm::Unknown; +} + +std::string GpgSignatureVerification::getSignerName() const +{ + if (!hasValidResult(gpgResult)) { + return {}; + } + + const auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + return {}; + } + if (protocol == GpgME::CMS) { + const auto dn = DN::parseString(fromCharPtr(signature->key(true, false).userID(0).id())); + return DN::FindFirstValue(dn, "CN").value_or(""); + } else if (protocol == GpgME::OpenPGP) { + return fromCharPtr(signature->key(true, false).userID(0).name()); + } + + return {}; +} + +std::string GpgSignatureVerification::getSignerSubjectDN() const +{ + if (!hasValidResult(gpgResult)) { + return {}; + } + const auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + return {}; + } + return fromCharPtr(signature->key(true, false).userID(0).id()); +} + +std::chrono::system_clock::time_point GpgSignatureVerification::getSigningTime() const +{ + if (!hasValidResult(gpgResult)) { + return {}; + } + const auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + return {}; + } + return std::chrono::system_clock::from_time_t(signature->creationTime()); +} + +void GpgSignatureVerification::validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function &doneFunction) +{ + cachedValidationStatus.reset(); + if (!gpgResult) { + validationStatus = std::async([doneFunction]() { + if (doneFunction) { + doneFunction(); + } + return CERTIFICATE_NOT_VERIFIED; + }); + return; + } + if (gpgResult->error()) { + validationStatus = std::async([doneFunction]() { + if (doneFunction) { + doneFunction(); + } + return CERTIFICATE_GENERIC_ERROR; + }); + return; + } + const auto signature = getSignature(gpgResult.value(), 0); + if (!signature) { + validationStatus = std::async([doneFunction]() { + if (doneFunction) { + doneFunction(); + } + return CERTIFICATE_GENERIC_ERROR; + }); + return; + } + std::string keyFP = fromCharPtr(signature->key().primaryFingerprint()); + validationStatus = std::async([keyFP = std::move(keyFP), protocol = protocol, doneFunction, ocspRevocationCheck, useAIACertFetch]() { + auto context = GpgME::Context::create(protocol); + context->setOffline((!ocspRevocationCheck) || useAIACertFetch); + context->setKeyListMode(GpgME::KeyListMode::Local | GpgME::KeyListMode::Validate); + GpgME::Error e; + const auto key = context->key(keyFP.c_str(), e, false); + if (doneFunction) { + doneFunction(); + } + if (e.isCanceled()) { + return CERTIFICATE_NOT_VERIFIED; + } + if (e) { + return CERTIFICATE_GENERIC_ERROR; + } + if (key.isExpired()) { + return CERTIFICATE_EXPIRED; + } + if (key.isRevoked()) { + return CERTIFICATE_REVOKED; + } + if (key.isBad()) { + return CERTIFICATE_NOT_VERIFIED; + } + return CERTIFICATE_TRUSTED; + }); +} + +CertificateValidationStatus GpgSignatureVerification::validateCertificateResult() +{ + if (cachedValidationStatus) { + return cachedValidationStatus.value(); + } + if (!validationStatus.valid()) { + return CERTIFICATE_NOT_VERIFIED; + } + validationStatus.wait(); + cachedValidationStatus = validationStatus.get(); + return cachedValidationStatus.value(); +} + +SignatureValidationStatus GpgSignatureVerification::validateSignature() +{ + signedData.rewind(); + const auto result = gpgContext->verifyDetachedSignature(signatureData, signedData); + gpgResult = result; + + if (!isValidResult(result)) { + return SIGNATURE_DECODING_ERROR; + } + const auto signature = getSignature(result, 0); + if (!signature) { + return SIGNATURE_DECODING_ERROR; + } + switch (signature->status().code()) { + case GPG_ERR_NO_ERROR: + case GPG_ERR_CERT_EXPIRED: // was valid + case GPG_ERR_SIG_EXPIRED: // was valid + case GPG_ERR_CERT_REVOKED: // was valid + return SIGNATURE_VALID; + case GPG_ERR_BAD_SIGNATURE: + return SIGNATURE_INVALID; + default: + return SIGNATURE_GENERIC_ERROR; + } +} + +#ifdef ENABLE_PGP_SIGNATURES +bool GpgSignatureConfiguration::allowPgp = true; +#else +bool GpgSignatureConfiguration::allowPgp = false; +#endif +bool GpgSignatureConfiguration::arePgpSignaturesAllowed() +{ + return allowPgp; +} + +void GpgSignatureConfiguration::setPgpSignaturesAllowed(bool allow) +{ + allowPgp = allow; +} + +std::vector GpgSignatureConfiguration::allowedTypes() +{ + std::vector allowedTypes; + allowedTypes.push_back(GpgME::CMS); + if (allowPgp) { + allowedTypes.push_back(GpgME::OpenPGP); + } + return allowedTypes; +} diff --git a/poppler/GPGMECryptoSignBackend.h b/poppler/GPGMECryptoSignBackend.h new file mode 100644 index 0000000..ede86ad --- /dev/null +++ b/poppler/GPGMECryptoSignBackend.h @@ -0,0 +1,83 @@ +//======================================================================== +// +// GPGMECryptoSignBackend.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#include "CryptoSignBackend.h" +#include "poppler_private_export.h" + +#include +#include +#include +#include + +#define DUMP_SIGNATURE_DATA 0 + +class GpgSignatureBackend : public CryptoSign::Backend +{ +public: + GpgSignatureBackend(); + std::unique_ptr createVerificationHandler(std::vector &&pkcs7, CryptoSign::SignatureType type) final; + std::unique_ptr createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) final; + std::vector> getAvailableSigningCertificates() final; + static bool hasSufficientVersion(); +}; + +class GpgSignatureCreation : public CryptoSign::SigningInterface +{ +public: + GpgSignatureCreation(const std::string &certId); + void addData(unsigned char *dataBlock, int dataLen) final; + std::unique_ptr getCertificateInfo() const final; + std::variant, CryptoSign::SigningError> signDetached(const std::string &password) final; + CryptoSign::SignatureType signatureType() const final; + +private: + std::unique_ptr gpgContext; + GpgME::Data gpgData; + std::optional key; + GpgME::Protocol protocol; +}; + +class GpgSignatureVerification : public CryptoSign::VerificationInterface +{ +public: + explicit GpgSignatureVerification(const std::vector &pkcs7data, GpgME::Protocol protocol); + SignatureValidationStatus validateSignature() final; + void addData(unsigned char *dataBlock, int dataLen) final; + std::chrono::system_clock::time_point getSigningTime() const final; + std::string getSignerName() const final; + std::string getSignerSubjectDN() const final; + HashAlgorithm getHashAlgorithm() const final; + CertificateValidationStatus validateCertificateResult() final; + void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function &doneCallback) final; + std::unique_ptr getCertificateInfo() const final; + +private: + std::unique_ptr gpgContext; + GpgME::Data signatureData; + GpgME::Data signedData; + std::optional gpgResult; + std::future validationStatus; + std::optional cachedValidationStatus; + GpgME::Protocol protocol; +#if DUMP_SIGNATURE_DATA + std::unique_ptr debugSignedData; +#endif +}; + +class POPPLER_PRIVATE_EXPORT GpgSignatureConfiguration +{ +public: + static bool arePgpSignaturesAllowed(); + static void setPgpSignaturesAllowed(bool allowed); + static std::vector allowedTypes(); + GpgSignatureConfiguration() = delete; + +private: + static bool allowPgp; +}; diff --git a/poppler/Gfx.cc b/poppler/Gfx.cc index 17202a0..46c77c4 100644 --- a/poppler/Gfx.cc +++ b/poppler/Gfx.cc @@ -14,13 +14,13 @@ // under GPL version 2 or later // // Copyright (C) 2005 Jonathan Blandford -// Copyright (C) 2005-2013, 2015-2021 Albert Astals Cid +// Copyright (C) 2005-2013, 2015-2022, 2024, 2025 Albert Astals Cid // Copyright (C) 2006 Thorkild Stray // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2006-2011 Carlos Garcia Campos // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2007, 2008 Brad Hards -// Copyright (C) 2007, 2011, 2017, 2021 Adrian Johnson +// Copyright (C) 2007, 2011, 2017, 2021, 2023 Adrian Johnson // Copyright (C) 2007, 2008 Iñigo Martínez // Copyright (C) 2007 Koji Otani // Copyright (C) 2007 Krzysztof Kowalczyk @@ -34,7 +34,7 @@ // Copyright (C) 2010 Nils Höglund // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Axel Strübing -// Copyright (C) 2012 Even Rouault +// Copyright (C) 2012, 2024 Even Rouault // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2012 Lu Wang // Copyright (C) 2014 Jason Crain @@ -42,10 +42,14 @@ // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2018 Denis Onishchenko // Copyright (C) 2019 LE GARREC Vincent -// Copyright (C) 2019-2022 Oliver Sander +// Copyright (C) 2019-2022, 2024 Oliver Sander // Copyright (C) 2019 Volker Krause // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Steve Rosenhamer +// Copyright (C) 2023 Anton Thomasson +// Copyright (C) 2024 Nelson Benítez León +// Copyright (C) 2024 Athul Raj Kollareth +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -76,12 +80,14 @@ #include "GfxState.h" #include "OutputDev.h" #include "Page.h" -#include "Annot.h" #include "Error.h" #include "Gfx.h" #include "ProfileData.h" #include "Catalog.h" #include "OptionalContent.h" +#ifdef ENABLE_LIBOPENJPEG +# include "JPEG2000Stream.h" +#endif // the MSVC math.h doesn't define this #ifndef M_PI @@ -238,22 +244,14 @@ static inline bool isSameGfxColor(const GfxColor &colorA, const GfxColor &colorB GfxResources::GfxResources(XRef *xrefA, Dict *resDictA, GfxResources *nextA) : gStateCache(2), xref(xrefA) { - Ref r; - if (resDictA) { // build font dictionary Dict *resDict = resDictA->copy(xref); - fonts = nullptr; - const Object &obj1 = resDict->lookupNF("Font"); - if (obj1.isRef()) { - Object obj2 = obj1.fetch(xref); - if (obj2.isDict()) { - r = obj1.getRef(); - fonts = new GfxFontDict(xref, &r, obj2.getDict()); - } - } else if (obj1.isDict()) { - fonts = new GfxFontDict(xref, nullptr, obj1.getDict()); + Ref fontDictRef; + const Object &fontDictObj = resDict->lookup("Font", &fontDictRef); + if (fontDictObj.isDict()) { + fonts = std::make_unique(xref, fontDictRef, fontDictObj.getDict()); } // get XObject dictionary @@ -288,10 +286,7 @@ GfxResources::GfxResources(XRef *xrefA, Dict *resDictA, GfxResources *nextA) : g next = nextA; } -GfxResources::~GfxResources() -{ - delete fonts; -} +GfxResources::~GfxResources() = default; std::shared_ptr GfxResources::doLookupFont(const char *name) const { @@ -381,7 +376,7 @@ Object GfxResources::lookupColorSpace(const char *name) return Object(objNull); } -GfxPattern *GfxResources::lookupPattern(const char *name, OutputDev *out, GfxState *state) +std::unique_ptr GfxResources::lookupPattern(const char *name, OutputDev *out, GfxState *state) { GfxResources *resPtr; @@ -395,25 +390,23 @@ GfxPattern *GfxResources::lookupPattern(const char *name, OutputDev *out, GfxSta } } error(errSyntaxError, -1, "Unknown pattern '{0:s}'", name); - return nullptr; + return {}; } -GfxShading *GfxResources::lookupShading(const char *name, OutputDev *out, GfxState *state) +std::unique_ptr GfxResources::lookupShading(const char *name, OutputDev *out, GfxState *state) { GfxResources *resPtr; - GfxShading *shading; for (resPtr = this; resPtr; resPtr = resPtr->next) { if (resPtr->shadingDict.isDict()) { Object obj = resPtr->shadingDict.dictLookup(name); if (!obj.isNull()) { - shading = GfxShading::parse(resPtr, &obj, out, state); - return shading; + return GfxShading::parse(resPtr, &obj, out, state); } } } error(errSyntaxError, -1, "ExtGState '{0:s}' is unknown", name); - return nullptr; + return {}; } Object GfxResources::lookupGState(const char *name) @@ -602,7 +595,7 @@ void Gfx::initDisplayProfile() Gfx::~Gfx() { - while (stateGuards.size()) { + while (!stateGuards.empty()) { popStateGuard(); } if (!subPage) { @@ -1140,7 +1133,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) if (obj3.isStream()) { Object obj4 = obj3.streamGetDict()->lookup("Group"); if (obj4.isDict()) { - GfxColorSpace *blendingColorSpace = nullptr; + std::unique_ptr blendingColorSpace; Object obj5 = obj4.dictLookup("CS"); if (!obj5.isNull()) { blendingColorSpace = GfxColorSpace::parse(res, &obj5, out, state); @@ -1157,8 +1150,7 @@ void Gfx::opSetExtGState(Object args[], int numArgs) } } } - doSoftMask(&obj3, alpha, blendingColorSpace, isolated, knockout, softMaskTransferFunc, &backdropColor); - delete blendingColorSpace; + doSoftMask(&obj3, alpha, blendingColorSpace.get(), isolated, knockout, softMaskTransferFunc, &backdropColor); } else { error(errSyntaxError, getPos(), "Invalid soft mask in ExtGState - missing group"); } @@ -1300,18 +1292,17 @@ void Gfx::opSetRenderingIntent(Object args[], int numArgs) void Gfx::opSetFillGray(Object args[], int numArgs) { GfxColor color; - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; state->setFillPattern(nullptr); Object obj = res->lookupColorSpace("DefaultGray"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr || colorSpace->getNComps() > 1) { - delete colorSpace; + if (!colorSpace || colorSpace->getNComps() > 1) { colorSpace = state->copyDefaultGrayColorSpace(); } - state->setFillColorSpace(colorSpace); + state->setFillColorSpace(std::move(colorSpace)); out->updateFillColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setFillColor(&color); @@ -1321,17 +1312,17 @@ void Gfx::opSetFillGray(Object args[], int numArgs) void Gfx::opSetStrokeGray(Object args[], int numArgs) { GfxColor color; - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; state->setStrokePattern(nullptr); Object obj = res->lookupColorSpace("DefaultGray"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr) { + if (!colorSpace) { colorSpace = state->copyDefaultGrayColorSpace(); } - state->setStrokeColorSpace(colorSpace); + state->setStrokeColorSpace(std::move(colorSpace)); out->updateStrokeColorSpace(state); color.c[0] = dblToCol(args[0].getNum()); state->setStrokeColor(&color); @@ -1341,18 +1332,18 @@ void Gfx::opSetStrokeGray(Object args[], int numArgs) void Gfx::opSetFillCMYKColor(Object args[], int numArgs) { GfxColor color; - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; int i; Object obj = res->lookupColorSpace("DefaultCMYK"); if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr) { + if (!colorSpace) { colorSpace = state->copyDefaultCMYKColorSpace(); } state->setFillPattern(nullptr); - state->setFillColorSpace(colorSpace); + state->setFillColorSpace(std::move(colorSpace)); out->updateFillColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1364,7 +1355,7 @@ void Gfx::opSetFillCMYKColor(Object args[], int numArgs) void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) { GfxColor color; - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; int i; state->setStrokePattern(nullptr); @@ -1372,10 +1363,10 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr) { + if (!colorSpace) { colorSpace = state->copyDefaultCMYKColorSpace(); } - state->setStrokeColorSpace(colorSpace); + state->setStrokeColorSpace(std::move(colorSpace)); out->updateStrokeColorSpace(state); for (i = 0; i < 4; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1386,7 +1377,7 @@ void Gfx::opSetStrokeCMYKColor(Object args[], int numArgs) void Gfx::opSetFillRGBColor(Object args[], int numArgs) { - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; GfxColor color; int i; @@ -1395,11 +1386,10 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr || colorSpace->getNComps() > 3) { - delete colorSpace; + if (!colorSpace || colorSpace->getNComps() > 3) { colorSpace = state->copyDefaultRGBColorSpace(); } - state->setFillColorSpace(colorSpace); + state->setFillColorSpace(std::move(colorSpace)); out->updateFillColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1410,7 +1400,7 @@ void Gfx::opSetFillRGBColor(Object args[], int numArgs) void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) { - GfxColorSpace *colorSpace = nullptr; + std::unique_ptr colorSpace; GfxColor color; int i; @@ -1419,10 +1409,10 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) if (!obj.isNull()) { colorSpace = GfxColorSpace::parse(res, &obj, out, state); } - if (colorSpace == nullptr) { + if (!colorSpace) { colorSpace = state->copyDefaultRGBColorSpace(); } - state->setStrokeColorSpace(colorSpace); + state->setStrokeColorSpace(std::move(colorSpace)); out->updateStrokeColorSpace(state); for (i = 0; i < 3; ++i) { color.c[i] = dblToCol(args[i].getNum()); @@ -1433,7 +1423,7 @@ void Gfx::opSetStrokeRGBColor(Object args[], int numArgs) void Gfx::opSetFillColorSpace(Object args[], int numArgs) { - GfxColorSpace *colorSpace; + std::unique_ptr colorSpace; GfxColor color; Object obj = res->lookupColorSpace(args[0].getName()); @@ -1444,9 +1434,9 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) } if (colorSpace) { state->setFillPattern(nullptr); - state->setFillColorSpace(colorSpace); + state->setFillColorSpace(std::move(colorSpace)); out->updateFillColorSpace(state); - colorSpace->getDefaultColor(&color); + state->getFillColorSpace()->getDefaultColor(&color); state->setFillColor(&color); out->updateFillColor(state); } else { @@ -1456,7 +1446,7 @@ void Gfx::opSetFillColorSpace(Object args[], int numArgs) void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) { - GfxColorSpace *colorSpace; + std::unique_ptr colorSpace; GfxColor color; state->setStrokePattern(nullptr); @@ -1467,9 +1457,9 @@ void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) colorSpace = GfxColorSpace::parse(res, &obj, out, state); } if (colorSpace) { - state->setStrokeColorSpace(colorSpace); + state->setStrokeColorSpace(std::move(colorSpace)); out->updateStrokeColorSpace(state); - colorSpace->getDefaultColor(&color); + state->getStrokeColorSpace()->getDefaultColor(&color); state->setStrokeColor(&color); out->updateStrokeColor(state); } else { @@ -1514,7 +1504,6 @@ void Gfx::opSetStrokeColor(Object args[], int numArgs) void Gfx::opSetFillColorN(Object args[], int numArgs) { GfxColor color; - GfxPattern *pattern; int i; if (state->getFillColorSpace()->getMode() == csPattern) { @@ -1534,8 +1523,9 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) out->updateFillColor(state); } if (numArgs > 0) { + std::unique_ptr pattern; if (args[numArgs - 1].isName() && (pattern = res->lookupPattern(args[numArgs - 1].getName(), out, state))) { - state->setFillPattern(pattern); + state->setFillPattern(std::move(pattern)); } } @@ -1560,7 +1550,6 @@ void Gfx::opSetFillColorN(Object args[], int numArgs) void Gfx::opSetStrokeColorN(Object args[], int numArgs) { GfxColor color; - GfxPattern *pattern; int i; if (state->getStrokeColorSpace()->getMode() == csPattern) { @@ -1583,8 +1572,9 @@ void Gfx::opSetStrokeColorN(Object args[], int numArgs) error(errSyntaxError, getPos(), "Incorrect number of arguments in 'SCN' command"); return; } + std::unique_ptr pattern; if (args[numArgs - 1].isName() && (pattern = res->lookupPattern(args[numArgs - 1].getName(), out, state))) { - state->setStrokePattern(pattern); + state->setStrokePattern(std::move(pattern)); } } else { @@ -1976,7 +1966,6 @@ void Gfx::doPatternImageMask(Object *ref, Stream *str, int width, int height, bo void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, bool stroke, bool eoFill, bool text) { GfxPatternColorSpace *patCS; - GfxColorSpace *cs; GfxColor color; GfxState *savedState; double xMin, yMin, xMax, yMax, x, y, x1, y1; @@ -2045,7 +2034,8 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, bool stroke, bool eoFill, // Adobe's behavior state->setFillPattern(nullptr); state->setStrokePattern(nullptr); - if (tPat->getPaintType() == 2 && (cs = patCS->getUnder())) { + if (tPat->getPaintType() == 2 && patCS->getUnder()) { + GfxColorSpace *cs = patCS->getUnder(); state->setFillColorSpace(cs->copy()); out->updateFillColorSpace(state); state->setStrokeColorSpace(cs->copy()); @@ -2058,12 +2048,11 @@ void Gfx::doTilingPatternFill(GfxTilingPattern *tPat, bool stroke, bool eoFill, out->updateFillColor(state); out->updateStrokeColor(state); } else { - cs = new GfxDeviceGrayColorSpace(); - state->setFillColorSpace(cs); - cs->getDefaultColor(&color); + state->setFillColorSpace(std::make_unique()); + state->getFillColorSpace()->getDefaultColor(&color); state->setFillColor(&color); out->updateFillColorSpace(state); - state->setStrokeColorSpace(new GfxDeviceGrayColorSpace()); + state->setStrokeColorSpace(std::make_unique()); state->setStrokeColor(&color); out->updateStrokeColorSpace(state); } @@ -2302,21 +2291,21 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, bool stroke, bool eoFill // do shading type-specific operations switch (shading->getType()) { - case 1: + case GfxShading::FunctionBasedShading: doFunctionShFill((GfxFunctionShading *)shading); break; - case 2: + case GfxShading::AxialShading: doAxialShFill((GfxAxialShading *)shading); break; - case 3: + case GfxShading::RadialShading: doRadialShFill((GfxRadialShading *)shading); break; - case 4: - case 5: + case GfxShading::FreeFormGouraudShadedTriangleMesh: + case GfxShading::LatticeFormGouraudShadedTriangleMesh: doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); break; - case 6: - case 7: + case GfxShading::CoonsPatchMesh: + case GfxShading::TensorProductPatchMesh: doPatchMeshShFill((GfxPatchMeshShading *)shading); break; } @@ -2333,7 +2322,7 @@ void Gfx::doShadingPatternFill(GfxShadingPattern *sPat, bool stroke, bool eoFill void Gfx::opShFill(Object args[], int numArgs) { - GfxShading *shading; + std::unique_ptr shading; GfxState *savedState; double xMin, yMin, xMax, yMax; @@ -2374,22 +2363,22 @@ void Gfx::opShFill(Object args[], int numArgs) // do shading type-specific operations switch (shading->getType()) { - case 1: - doFunctionShFill((GfxFunctionShading *)shading); + case GfxShading::FunctionBasedShading: + doFunctionShFill((GfxFunctionShading *)shading.get()); break; - case 2: - doAxialShFill((GfxAxialShading *)shading); + case GfxShading::AxialShading: + doAxialShFill((GfxAxialShading *)shading.get()); break; - case 3: - doRadialShFill((GfxRadialShading *)shading); + case GfxShading::RadialShading: + doRadialShFill((GfxRadialShading *)shading.get()); break; - case 4: - case 5: - doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading); + case GfxShading::FreeFormGouraudShadedTriangleMesh: + case GfxShading::LatticeFormGouraudShadedTriangleMesh: + doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading.get()); break; - case 6: - case 7: - doPatchMeshShFill((GfxPatchMeshShading *)shading); + case GfxShading::CoonsPatchMesh: + case GfxShading::TensorProductPatchMesh: + doPatchMeshShFill((GfxPatchMeshShading *)shading.get()); break; } @@ -2401,8 +2390,6 @@ void Gfx::opShFill(Object args[], int numArgs) // restore graphics state restoreStateStack(savedState); - - delete shading; } void Gfx::doFunctionShFill(GfxFunctionShading *shading) @@ -2559,7 +2546,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) bboxIntersections[1] = ((xMin - x0) * dx + (yMax - y0) * dy) * mul; bboxIntersections[2] = ((xMax - x0) * dx + (yMin - y0) * dy) * mul; bboxIntersections[3] = ((xMax - x0) * dx + (yMax - y0) * dy) * mul; - std::sort(std::begin(bboxIntersections), std::end(bboxIntersections)); + std::ranges::sort(bboxIntersections); tMin = bboxIntersections[0]; tMax = bboxIntersections[3]; if (tMin < 0 && !shading->getExtend0()) { @@ -2660,7 +2647,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) s[1] = (yMax - ty) / dx; s[2] = (xMin - tx) / -dy; s[3] = (xMax - tx) / -dy; - std::sort(std::begin(s), std::end(s)); + std::ranges::sort(s); sMin = s[1]; sMax = s[2]; } @@ -2801,7 +2788,7 @@ void Gfx::doAxialShFill(GfxAxialShading *shading) s[1] = (yMax - ty) / dx; s[2] = (xMin - tx) / -dy; s[3] = (xMax - tx) / -dy; - std::sort(std::begin(s), std::end(s)); + std::ranges::sort(s); sMin = s[1]; sMax = s[2]; } @@ -3251,7 +3238,7 @@ void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) state->lineTo(0., 1.); state->closePath(); - GfxState::ReusablePathIterator *reusablePath = state->getReusablePath(); + const std::unique_ptr reusablePath = state->getReusablePath(); if (shading->isParameterized()) { // work with parameterized values: @@ -3260,7 +3247,7 @@ void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) const double refineColorThreshold = gouraudParameterizedColorDelta * (shading->getParameterDomainMax() - shading->getParameterDomainMin()); for (i = 0; i < shading->getNTriangles(); ++i) { shading->getTriangle(i, &x0, &y0, &color0, &x1, &y1, &color1, &x2, &y2, &color2); - gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath); + gouraudFillTriangle(x0, y0, color0, x1, y1, color1, x2, y2, color2, refineColorThreshold, 0, shading, reusablePath.get()); } } else { @@ -3273,11 +3260,9 @@ void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) GfxColor color0, color1, color2; for (i = 0; i < shading->getNTriangles(); ++i) { shading->getTriangle(i, &x0, &y0, &color0, &x1, &y1, &color1, &x2, &y2, &color2); - gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath); + gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2, shading->getColorSpace()->getNComps(), 0, reusablePath.get()); } } - - delete reusablePath; } static inline void checkTrue(bool b, const char *message) @@ -3619,7 +3604,8 @@ void Gfx::opSetFont(Object args[], int numArgs) return; } if (printCommands) { - printf(" font: tag=%s name='%s' %g\n", font->getTag().c_str(), font->getName() ? font->getName()->c_str() : "???", args[1].getNum()); + const std::optional &fontName = font->getName(); + printf(" font: tag=%s name='%s' %g\n", font->getTag().c_str(), fontName ? fontName->c_str() : "???", args[1].getNum()); fflush(stdout); } @@ -4134,9 +4120,9 @@ void Gfx::opXObject(Object args[], int numArgs) out->drawForm(refObj.getRef()); } else { Ref ref = refObj.isRef() ? refObj.getRef() : Ref::INVALID(); - out->beginForm(ref); + out->beginForm(&obj1, ref); doForm(&obj1); - out->endForm(ref); + out->endForm(&obj1, ref); } } if (refObj.isRef() && shouldDoForm) { @@ -4166,19 +4152,25 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) StreamColorSpaceMode csMode; bool mask; bool invert; - GfxColorSpace *colorSpace, *maskColorSpace; bool haveColorKeyMask, haveExplicitMask, haveSoftMask; int maskColors[2 * gfxColorMaxComps] = {}; int maskWidth, maskHeight; bool maskInvert; bool maskInterpolate; + bool hasAlpha; Stream *maskStr; int i, n; // get info from the stream bits = 0; csMode = streamCSNone; - str->getImageParams(&bits, &csMode); +#ifdef ENABLE_LIBOPENJPEG + if (str->getKind() == strJPX && out->supportJPXtransparency()) { + JPXStream *jpxStream = dynamic_cast(str); + jpxStream->setSupportJPXtransparency(true); + } +#endif + str->getImageParams(&bits, &csMode, &hasAlpha); // get stream dict dict = str->getDict(); @@ -4191,6 +4183,11 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) } } + const double *ctm = state->getCTM(); + const double det = ctm[0] * ctm[3] - ctm[1] * ctm[2]; + // Detect singular matrix (non invertible) to avoid drawing Image in such case + const bool singular_matrix = fabs(det) < 0.000001; + // get size Object obj1 = dict->lookup("Width"); if (obj1.isNull()) { @@ -4215,7 +4212,7 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) goto err1; } - if (width < 1 || height < 1) { + if (width < 1 || height < 1 || width > INT_MAX / height) { goto err1; } @@ -4284,7 +4281,9 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) // if drawing is disabled, skip over inline image data if (!ocState || !out->needNonText()) { - str->reset(); + if (!str->reset()) { + goto err1; + } n = height * ((width + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); @@ -4309,13 +4308,30 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) if (obj1.isNull()) { obj1 = dict->lookup("CS"); } + bool haveColorSpace = !obj1.isNull(); + bool haveRGBA = false; + if (str->getKind() == strJPX && out->supportJPXtransparency() && (csMode == streamCSDeviceRGB || csMode == streamCSDeviceCMYK)) { + // Case of transparent JPX image, they may contain RGBA data + // when have no ColorSpace or when SMaskInData=1 · Issue #1486 + if (!haveColorSpace) { + haveRGBA = hasAlpha; + } else { + Object smaskInData = dict->lookup("SMaskInData"); + if (smaskInData.isInt() && smaskInData.getInt()) { + haveRGBA = true; + } + } + } + if (obj1.isName() && inlineImg) { Object obj2 = res->lookupColorSpace(obj1.getName()); if (!obj2.isNull()) { obj1 = std::move(obj2); } } - if (!obj1.isNull()) { + std::unique_ptr colorSpace; + + if (!obj1.isNull() && !haveRGBA) { char *tempIntent = nullptr; Object objIntent = dict->lookup("Intent"); if (objIntent.isName()) { @@ -4333,26 +4349,32 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) } else if (csMode == streamCSDeviceGray) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { - colorSpace = new GfxDeviceGrayColorSpace(); + colorSpace = std::make_unique(); } else { colorSpace = GfxColorSpace::parse(res, &objCS, out, state); } } else if (csMode == streamCSDeviceRGB) { - Object objCS = res->lookupColorSpace("DefaultRGB"); - if (objCS.isNull()) { - colorSpace = new GfxDeviceRGBColorSpace(); + if (haveRGBA) { + colorSpace = std::make_unique(); } else { - colorSpace = GfxColorSpace::parse(res, &objCS, out, state); + Object objCS = res->lookupColorSpace("DefaultRGB"); + if (objCS.isNull()) { + colorSpace = std::make_unique(); + } else { + colorSpace = GfxColorSpace::parse(res, &objCS, out, state); + } } } else if (csMode == streamCSDeviceCMYK) { - Object objCS = res->lookupColorSpace("DefaultCMYK"); - if (objCS.isNull()) { - colorSpace = new GfxDeviceCMYKColorSpace(); + if (haveRGBA) { + colorSpace = std::make_unique(); } else { - colorSpace = GfxColorSpace::parse(res, &objCS, out, state); + Object objCS = res->lookupColorSpace("DefaultCMYK"); + if (objCS.isNull()) { + colorSpace = std::make_unique(); + } else { + colorSpace = GfxColorSpace::parse(res, &objCS, out, state); + } } - } else { - colorSpace = nullptr; } if (!colorSpace) { goto err1; @@ -4361,7 +4383,7 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) if (obj1.isNull()) { obj1 = dict->lookup("D"); } - GfxImageColorMap colorMap(bits, &obj1, colorSpace); + GfxImageColorMap colorMap(bits, &obj1, std::move(colorSpace)); if (!colorMap.isOk()) { goto err1; } @@ -4459,25 +4481,24 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) if (!obj1.isName("DeviceGray") && !obj1.isName("G")) { goto err1; } - maskColorSpace = new GfxDeviceGrayColorSpace(); obj1 = maskDict->lookup("Decode"); if (obj1.isNull()) { obj1 = maskDict->lookup("D"); } - maskColorMap = std::make_unique(maskBits, &obj1, maskColorSpace); + maskColorMap = std::make_unique(maskBits, &obj1, std::make_unique()); if (!maskColorMap->isOk()) { goto err1; } // handle the Matte entry obj1 = maskDict->lookup("Matte"); if (obj1.isArray()) { - if (obj1.getArray()->getLength() != colorSpace->getNComps()) { - error(errSyntaxError, -1, "Matte entry should have {0:d} components but has {1:d}", colorSpace->getNComps(), obj1.getArray()->getLength()); + if (obj1.getArray()->getLength() != colorMap.getColorSpace()->getNComps()) { + error(errSyntaxError, -1, "Matte entry should have {0:d} components but has {1:d}", colorMap.getColorSpace()->getNComps(), obj1.getArray()->getLength()); } else if (maskWidth != width || maskHeight != height) { error(errSyntaxError, -1, "Softmask with matte entry {0:d} x {1:d} must have same geometry as the image {2:d} x {3:d}", maskWidth, maskHeight, width, height); } else { GfxColor matteColor; - for (i = 0; i < colorSpace->getNComps(); i++) { + for (i = 0; i < colorMap.getColorSpace()->getNComps(); i++) { Object obj2 = obj1.getArray()->get(i); if (!obj2.isNum()) { error(errSyntaxError, -1, "Matte entry {0:d} should be a number but it's of type {1:d}", i, obj2.getType()); @@ -4486,7 +4507,7 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) } matteColor.c[i] = dblToCol(obj2.getNum()); } - if (i == colorSpace->getNComps()) { + if (i == colorMap.getColorSpace()->getNComps()) { maskColorMap->setMatteColor(&matteColor); } } @@ -4571,8 +4592,10 @@ void Gfx::doImage(Object *ref, Stream *str, bool inlineImg) } // if drawing is disabled, skip over inline image data - if (!ocState || !out->needNonText()) { - str->reset(); + if (!ocState || !out->needNonText() || singular_matrix) { + if (!str->reset()) { + goto err1; + } n = height * ((width * colorMap.getNumPixelComps() * colorMap.getBits() + 7) / 8); for (i = 0; i < n; ++i) { str->getChar(); @@ -4670,7 +4693,6 @@ void Gfx::doForm(Object *str) { Dict *dict; bool transpGroup, isolated, knockout; - GfxColorSpace *blendingColorSpace; double m[6], bbox[4]; Dict *resDict; bool ocSaved; @@ -4740,7 +4762,7 @@ void Gfx::doForm(Object *str) // check for a transparency group transpGroup = isolated = knockout = false; - blendingColorSpace = nullptr; + std::unique_ptr blendingColorSpace; obj1 = dict->lookup("Group"); if (obj1.isDict()) { Object obj2 = obj1.dictLookup("S"); @@ -4762,11 +4784,7 @@ void Gfx::doForm(Object *str) } // draw it - drawForm(str, resDict, m, bbox, transpGroup, false, blendingColorSpace, isolated, knockout); - - if (blendingColorSpace) { - delete blendingColorSpace; - } + drawForm(str, resDict, m, bbox, transpGroup, false, blendingColorSpace.get(), isolated, knockout); ocState = ocSaved; } @@ -4869,8 +4887,6 @@ void Gfx::drawForm(Object *str, Dict *resDict, const double *matrix, const doubl } else if (transpGroup) { out->paintTransparencyGroup(state, bbox); } - - return; } //------------------------------------------------------------------------ @@ -5028,7 +5044,7 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) // push a new stack entry pushMarkedContent(); - OCGs *contentConfig = catalog->getOptContentConfig(); + const OCGs *contentConfig = catalog->getOptContentConfig(); const char *name0 = args[0].getName(); if (strncmp(name0, "OC", 2) == 0 && contentConfig) { if (numArgs >= 2) { @@ -5049,12 +5065,21 @@ void Gfx::opBeginMarkedContent(Object args[], int numArgs) } else { error(errSyntaxError, getPos(), "insufficient arguments for Marked Content"); } - } else if (args[0].isName("Span") && numArgs == 2 && args[1].isDict()) { - Object obj = args[1].dictLookup("ActualText"); - if (obj.isString()) { - out->beginActualText(state, obj.getString()); - MarkedContentStack *mc = mcStack; - mc->kind = gfxMCActualText; + } else if (args[0].isName("Span") && numArgs == 2) { + Object dictToUse; + if (args[1].isDict()) { + dictToUse = args[1].copy(); + } else if (args[1].isName()) { + dictToUse = res->lookupMarkedContentNF(args[1].getName()).fetch(xref); + } + + if (dictToUse.isDict()) { + Object obj = dictToUse.dictLookup("ActualText"); + if (obj.isString()) { + out->beginActualText(state, obj.getString()); + MarkedContentStack *mc = mcStack; + mc->kind = gfxMCActualText; + } } } @@ -5289,7 +5314,7 @@ void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor, double if (border && border->getWidth() > 0 && (!aColor || aColor->getSpace() != AnnotColor::colorTransparent)) { if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) { state->setStrokePattern(nullptr); - state->setStrokeColorSpace(new GfxDeviceRGBColorSpace()); + state->setStrokeColorSpace(std::make_unique()); out->updateStrokeColorSpace(state); } double r, g, b; @@ -5312,7 +5337,7 @@ void Gfx::drawAnnot(Object *str, AnnotBorder *border, AnnotColor *aColor, double state->setLineWidth(border->getWidth()); out->updateLineWidth(state); const std::vector &dash = border->getDash(); - if (border->getStyle() == AnnotBorder::borderDashed && dash.size() > 0) { + if (border->getStyle() == AnnotBorder::borderDashed && !dash.empty()) { std::vector dash2 = dash; state->setLineDash(std::move(dash2), 0); out->updateLineDash(state); @@ -5359,12 +5384,12 @@ void Gfx::restoreState() { if (stackHeight <= bottomGuard() || !state->hasSaves()) { error(errSyntaxError, -1, "Restoring state when no valid states to pop"); - commandAborted = true; return; } state = state->restore(); out->restoreState(state); stackHeight--; + clip = clipNone; } // Create a new state stack, and initialize it with a copy of the diff --git a/poppler/Gfx.h b/poppler/Gfx.h index 81a620b..a73beeb 100644 --- a/poppler/Gfx.h +++ b/poppler/Gfx.h @@ -17,14 +17,14 @@ // Copyright (C) 2007 Iñigo Martínez // Copyright (C) 2008 Brad Hards // Copyright (C) 2008, 2010 Carlos Garcia Campos -// Copyright (C) 2009-2013, 2017, 2018, 2021 Albert Astals Cid +// Copyright (C) 2009-2013, 2017, 2018, 2021, 2024 Albert Astals Cid // Copyright (C) 2009, 2010, 2012, 2013 Thomas Freitag // Copyright (C) 2010 David Benjamin // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2013 Fabio D'Urso // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019, 2022 Oliver Sander +// Copyright (C) 2019, 2022, 2024 Oliver Sander // Copyright (C) 2019 Volker Krause // // To see a description of the changes please see the Changelog file that @@ -123,8 +123,8 @@ class POPPLER_PRIVATE_EXPORT GfxResources Object lookupXObjectNF(const char *name); Object lookupMarkedContentNF(const char *name); Object lookupColorSpace(const char *name); - GfxPattern *lookupPattern(const char *name, OutputDev *out, GfxState *state); - GfxShading *lookupShading(const char *name, OutputDev *out, GfxState *state); + std::unique_ptr lookupPattern(const char *name, OutputDev *out, GfxState *state); + std::unique_ptr lookupShading(const char *name, OutputDev *out, GfxState *state); Object lookupGState(const char *name); Object lookupGStateNF(const char *name); @@ -133,7 +133,7 @@ class POPPLER_PRIVATE_EXPORT GfxResources private: std::shared_ptr doLookupFont(const char *name) const; - GfxFontDict *fonts; + std::unique_ptr fonts; Object xObjDict; Object colorSpaceDict; Object patternDict; @@ -166,10 +166,7 @@ class POPPLER_PRIVATE_EXPORT Gfx Gfx(const Gfx &) = delete; Gfx &operator=(const Gfx &other) = delete; - XRef *getXRef() - { - return xref; - } + XRef *getXRef() { return xref; } // Interpret a stream or array of streams. void display(Object *obj, bool topLevel = true); @@ -191,10 +188,7 @@ class POPPLER_PRIVATE_EXPORT Gfx void popStateGuard(); // Get the current graphics state object. - GfxState *getState() - { - return state; - } + GfxState *getState() { return state; } bool checkTransparencyGroup(Dict *resDict); diff --git a/poppler/GfxFont.cc b/poppler/GfxFont.cc index b0d0d61..fbc5473 100644 --- a/poppler/GfxFont.cc +++ b/poppler/GfxFont.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005, 2006, 2008-2010, 2012, 2014, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2006, 2008-2010, 2012, 2014, 2015, 2017-2024 Albert Astals Cid // Copyright (C) 2005, 2006 Kristian Høgsberg // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2007 Julien Rebetez @@ -35,7 +35,12 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 LE GARREC Vincent -// Copyright (C) 2021, 2022 Oliver Sander +// Copyright (C) 2021, 2022, 2024 Oliver Sander +// Copyright (C) 2023 Khaled Hosny +// Copyright (C) 2024 Nelson Benítez León +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Vincent Lefevre +// Copyright (C) 2024 G B // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -182,17 +187,6 @@ GfxFontLoc::GfxFontLoc(GfxFontLoc &&other) noexcept = default; GfxFontLoc &GfxFontLoc::operator=(GfxFontLoc &&other) noexcept = default; -void GfxFontLoc::setPath(GooString *pathA) -{ - path = pathA->toStr(); - delete pathA; -} - -const GooString *GfxFontLoc::pathAsGooString() const -{ - return (const GooString *)(&path); -} - //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ @@ -209,18 +203,34 @@ std::unique_ptr GfxFont::makeFont(XRef *xref, const char *tagA, Ref idA name = obj1.getName(); } + // There is no BaseFont in Type 3 fonts, try fontDescriptor.FontName + if (!name) { + Object fontDesc = fontDict->lookup("FontDescriptor"); + if (fontDesc.isDict()) { + Object obj2 = fontDesc.dictLookup("FontName"); + if (obj2.isName()) { + name = obj2.getName(); + } + } + } + + // As a last resort try the Name key + if (!name) { + Object obj2 = fontDict->lookup("Name"); + if (obj2.isName()) { + name = obj2.getName(); + } + } + // get embedded font ID and font type typeA = getFontType(xref, fontDict, &embFontIDA); // create the font object - GfxFont *font; if (typeA < fontCIDType0) { - font = new Gfx8BitFont(xref, tagA, idA, std::move(name), typeA, embFontIDA, fontDict); + return std::make_unique(xref, tagA, idA, std::move(name), typeA, embFontIDA, fontDict); } else { - font = new GfxCIDFont(xref, tagA, idA, std::move(name), typeA, embFontIDA, fontDict); + return std::make_unique(xref, tagA, idA, std::move(name), typeA, embFontIDA, fontDict); } - - return std::unique_ptr(font); } GfxFont::GfxFont(const char *tagA, Ref idA, std::optional &&nameA, GfxFontType typeA, Ref embFontIDA) : tag(tagA), id(idA), name(std::move(nameA)), type(typeA) @@ -234,13 +244,7 @@ GfxFont::GfxFont(const char *tagA, Ref idA, std::optional &&nameA, hasToUnicode = false; } -GfxFont::~GfxFont() -{ - delete family; - if (embFontName) { - delete embFontName; - } -} +GfxFont::~GfxFont() = default; bool GfxFont::isSubset() const { @@ -404,8 +408,7 @@ GfxFontType GfxFont::getFontType(XRef *xref, Dict *fontDict, Ref *embID) if (*embID != Ref::INVALID()) { Object obj3(*embID); Object obj4 = obj3.fetch(xref); - if (obj4.isStream()) { - obj4.streamReset(); + if (obj4.isStream() && obj4.streamReset()) { fft = FoFiIdentifier::identifyStream(&readFromStream, obj4.getStream()); obj4.streamClose(); switch (fft) { @@ -472,13 +475,13 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) // get name obj2 = obj1.dictLookup("FontName"); if (obj2.isName()) { - embFontName = new GooString(obj2.getName()); + embFontName = std::make_unique(obj2.getName()); } if (embFontName == nullptr) { // get name with typo obj2 = obj1.dictLookup("Fontname"); if (obj2.isName()) { - embFontName = new GooString(obj2.getName()); + embFontName = std::make_unique(obj2.getName()); error(errSyntaxWarning, -1, "The file uses Fontname instead of FontName please notify the creator that the file is broken"); } } @@ -486,7 +489,7 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) // get family obj2 = obj1.dictLookup("FontFamily"); if (obj2.isString()) { - family = new GooString(obj2.getString()); + family = std::make_unique(obj2.getString()); } // get stretch @@ -587,31 +590,30 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) } } -CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu) +std::unique_ptr GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits, std::unique_ptr ctu) { - GooString *buf; Object obj1 = fontDict->lookup("ToUnicode"); if (!obj1.isStream()) { - return nullptr; + return ctu; } - buf = new GooString(); - obj1.getStream()->fillGooString(buf); + auto buf = std::make_unique(); + obj1.getStream()->fillGooString(buf.get()); obj1.streamClose(); if (ctu) { - ctu->mergeCMap(buf, nBits); + ctu->mergeCMap(buf.get(), nBits); } else { - ctu = CharCodeToUnicode::parseCMap(buf, nBits); + ctu = CharCodeToUnicode::parseCMap(buf.get(), nBits); } hasToUnicode = true; - delete buf; return ctu; } -std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) +std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps, GooString *substituteFontName) { SysFontType sysFontType; - GooString *path, *base14Name; + std::optional path; + GooString *base14Name; int substIdx, fontNum; bool embed; @@ -657,7 +659,7 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) fontLoc.locType = gfxFontLocEmbedded; fontLoc.fontType = type; fontLoc.embFontID = embFontID; - return std::move(fontLoc); // std::move only required to please g++-7 + return fontLoc; } } } @@ -667,8 +669,8 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocResident; fontLoc.fontType = fontType1; - fontLoc.path = *name; - return std::move(fontLoc); // std::move only required to please g++-7 + fontLoc.path = name.value_or(std::string()); + return fontLoc; } //----- PS resident Base-14 font @@ -677,12 +679,12 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) fontLoc.locType = gfxFontLocResident; fontLoc.fontType = fontType1; fontLoc.path = ((Gfx8BitFont *)this)->base14->base14Name; - return std::move(fontLoc); // std::move only required to please g++-7 + return fontLoc; } //----- external font file (fontFile, fontDir) if (name && (path = globalParams->findFontFile(*name))) { - if (std::optional fontLoc = getExternalFont(path, isCIDFont())) { + if (std::optional fontLoc = getExternalFont(*path, isCIDFont())) { return fontLoc; } } @@ -690,8 +692,8 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) //----- external font file for Base-14 font if (!ps && !isCIDFont() && ((Gfx8BitFont *)this)->base14) { base14Name = new GooString(((Gfx8BitFont *)this)->base14->base14Name); - if ((path = globalParams->findBase14FontFile(base14Name, this))) { - if (std::optional fontLoc = getExternalFont(path, false)) { + if ((path = globalParams->findBase14FontFile(base14Name, this, substituteFontName))) { + if (std::optional fontLoc = getExternalFont(*path, false)) { delete base14Name; return fontLoc; } @@ -700,19 +702,19 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) } //----- system font - if ((path = globalParams->findSystemFontFile(this, &sysFontType, &fontNum))) { + if ((path = globalParams->findSystemFontFile(this, &sysFontType, &fontNum, substituteFontName))) { if (isCIDFont()) { if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocExternal; fontLoc.fontType = fontCIDType2; - fontLoc.setPath(path); + fontLoc.path = *path; fontLoc.fontNum = fontNum; - return std::move(fontLoc); // std::move only required to please g++-7 + return fontLoc; } } else { GfxFontLoc fontLoc; - fontLoc.setPath(path); + fontLoc.path = *path; fontLoc.locType = gfxFontLocExternal; if (sysFontType == sysFontTTF || sysFontType == sysFontTTC) { fontLoc.fontType = fontTrueType; @@ -720,9 +722,8 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) fontLoc.fontType = fontType1; fontLoc.fontNum = fontNum; } - return std::move(fontLoc); // std::move only required to please g++-7 + return fontLoc; } - delete path; } if (!isCIDFont()) { @@ -749,11 +750,11 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) fontLoc.fontType = fontType1; fontLoc.path = substName; fontLoc.substIdx = substIdx; - return std::move(fontLoc); // std::move only required to please g++-7 + return fontLoc; } else { path = globalParams->findFontFile(substName); if (path) { - if (std::optional fontLoc = getExternalFont(path, false)) { + if (std::optional fontLoc = getExternalFont(*path, false)) { error(errSyntaxWarning, -1, "Substituting font '{0:s}' for '{1:s}'", base14SubstFonts[substIdx], name ? name->c_str() : ""); name = base14SubstFonts[substIdx]; fontLoc->substIdx = substIdx; @@ -770,12 +771,12 @@ std::optional GfxFont::locateFont(XRef *xref, PSOutputDev *ps) return std::nullopt; } -std::optional GfxFont::getExternalFont(GooString *path, bool cid) +std::optional GfxFont::getExternalFont(const std::string &path, bool cid) { FoFiIdentifierType fft; GfxFontType fontType; - fft = FoFiIdentifier::identifyFile(path->c_str()); + fft = FoFiIdentifier::identifyFile(path.c_str()); switch (fft) { case fofiIdType1PFA: case fofiIdType1PFB: @@ -804,14 +805,13 @@ std::optional GfxFont::getExternalFont(GooString *path, bool cid) break; } if (fontType == fontUnknownType || (cid ? (fontType < fontCIDType0) : (fontType >= fontCIDType0))) { - delete path; return std::nullopt; } GfxFontLoc fontLoc; fontLoc.locType = gfxFontLocExternal; fontLoc.fontType = fontType; - fontLoc.setPath(path); - return std::move(fontLoc); // std::move only required to please g++-7 + fontLoc.path = path; + return fontLoc; } std::optional> GfxFont::readEmbFontFile(XRef *xref) @@ -961,8 +961,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("Encoding"); - if (obj1.isDict()) { + bool isZapfDingbats = name && name->ends_with("ZapfDingbats"); + if (isZapfDingbats) { + baseEnc = zapfDingbatsEncoding; + hasEncoding = true; + } else if (obj1.isDict()) { Object obj2 = obj1.dictLookup("BaseEncoding"); if (obj2.isName("MacRomanEncoding")) { hasEncoding = true; @@ -1120,11 +1122,9 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optional> buf = readEmbFontFile(xref); if (buf) { if ((ffT1 = FoFiType1::make(buf->data(), buf->size()))) { - if (ffT1->getName()) { - if (embFontName) { - delete embFontName; - } - embFontName = new GooString(ffT1->getName()); + const std::string fontName = ffT1->getName(); + if (!fontName.empty()) { + embFontName = std::make_unique(fontName); } if (!baseEnc) { baseEnc = (const char **)ffT1->getEncoding(); @@ -1137,10 +1137,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optionaldata(), buf->size()))) { if (ffT1C->getName()) { - if (embFontName) { - delete embFontName; - } - embFontName = new GooString(ffT1C->getName()); + embFontName = std::make_unique(ffT1C->getName()); } if (!baseEnc) { baseEnc = (const char **)ffT1C->getEncoding(); @@ -1232,7 +1229,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optionalmapNameToUnicodeText(charName); } - if (!toUnicode[code] && strcmp(charName, ".notdef")) { + if (!toUnicode[code] && (strcmp(charName, ".notdef") != 0)) { // if it wasn't in the name-to-Unicode table, check for a // name that looks like 'Axx' or 'xx', where 'A' is any letter // and 'xx' is two hex digits @@ -1282,7 +1278,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, const char *tagA, Ref idA, std::optionaldecRefCnt(); } // This function is in part a derived work of the Adobe Glyph Mapping @@ -1535,12 +1530,12 @@ int Gfx8BitFont::getNextChar(const char *s, int len, CharCode *code, Unicode con const CharCodeToUnicode *Gfx8BitFont::getToUnicode() const { - return ctu; + return ctu.get(); } -int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) +std::vector Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) { - int *map; + std::vector map; int cmapPlatform, cmapEncoding; int unicodeCmap, macRomanCmap, msSymbolCmap, cmap; bool useMacRoman, useUnicode; @@ -1548,10 +1543,7 @@ int *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) Unicode u; int code, i, n; - map = (int *)gmallocn(256, sizeof(int)); - for (i = 0; i < 256; ++i) { - map[i] = 0; - } + map.resize(256, 0); // To match up with the Adobe-defined behaviour, we choose a cmap // like this: @@ -1713,23 +1705,15 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("DescendantFonts"); @@ -1751,17 +1735,23 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("CIDSystemInfo"); - if (!obj1.isDict()) { + if (obj1.isDict()) { + obj2 = obj1.dictLookup("Registry"); + obj3 = obj1.dictLookup("Ordering"); + if (!obj2.isString() || !obj3.isString()) { + error(errSyntaxError, -1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); + error(errSyntaxError, -1, "Assuming Adobe-Identity for character collection"); + obj2 = Object(std::make_unique("Adobe")); + obj3 = Object(std::make_unique("Identity")); + } + collection = obj2.getString()->copy(); + collection->append('-'); + collection->append(obj3.getString()); + } else { error(errSyntaxError, -1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); - return; - } - obj2 = obj1.dictLookup("Registry"); - obj3 = obj1.dictLookup("Ordering"); - if (!obj2.isString() || !obj3.isString()) { - error(errSyntaxError, -1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); - return; + error(errSyntaxError, -1, "Assuming Adobe-Identity for character collection"); + collection = std::make_unique("Adobe-Identity"); } - collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); // look for a ToUnicode CMap if (!(ctu = readToUnicodeCMap(fontDict, 16, nullptr))) { @@ -1773,7 +1763,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionalgetCIDToUnicode(collection))) { + if (!(ctu = globalParams->getCIDToUnicode(collection.get()))) { // I'm not completely sure that this is the best thing to do // but it seems to produce better results when the .cidToUnicode // files from the poppler-data package are missing. At least @@ -1784,11 +1774,11 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionalcmp(knownCollection) == 0) { - error(errSyntaxError, -1, "Missing language pack for '{0:t}' mapping", collection); + error(errSyntaxError, -1, "Missing language pack for '{0:t}' mapping", collection.get()); return; } } - error(errSyntaxError, -1, "Unknown character collection '{0:t}'", collection); + error(errSyntaxError, -1, "Unknown character collection '{0:t}'", collection.get()); // fall-through, assuming the Identity mapping -- this appears // to match Adobe's behavior } @@ -1801,7 +1791,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionalgetCMapName()) { @@ -1812,17 +1802,9 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("CIDToGIDMap"); - if (obj1.isStream()) { - cidToGIDLen = 0; - unsigned int i = 64; - cidToGID = (int *)gmallocn(i, sizeof(int)); - obj1.streamReset(); + if (obj1.isStream() && obj1.streamReset()) { while ((c1 = obj1.streamGetChar()) != EOF && (c2 = obj1.streamGetChar()) != EOF) { - if (cidToGIDLen == i) { - i *= 2; - cidToGID = (int *)greallocn(cidToGID, i, sizeof(int)); - } - cidToGID[cidToGIDLen++] = (c1 << 8) + c2; + cidToGID.push_back((c1 << 8) + c2); } } else if (!obj1.isName("Identity") && !obj1.isNull()) { error(errSyntaxError, -1, "Invalid CIDToGIDMap entry in CID font"); @@ -1839,7 +1821,6 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("W"); if (obj1.isArray()) { - excepsSize = 0; int i = 0; while (i + 1 < obj1.arrayGetLength()) { obj2 = obj1.arrayGet(i); @@ -1847,33 +1828,21 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optional(obj2.getInt()), static_cast(obj3.getInt()), obj4.getNum() * 0.001 }; + widths.exceps.push_back(excep); } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); } i += 3; } else if (obj2.isInt() && obj3.isArray()) { - if (widths.nExceps + obj3.arrayGetLength() > excepsSize) { - excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15; - widths.exceps = (GfxFontCIDWidthExcep *)greallocn(widths.exceps, excepsSize, sizeof(GfxFontCIDWidthExcep)); - } int j = obj2.getInt(); if (likely(j < INT_MAX - obj3.arrayGetLength())) { for (int k = 0; k < obj3.arrayGetLength(); ++k) { obj4 = obj3.arrayGet(k); if (obj4.isNum()) { - widths.exceps[widths.nExceps].first = j; - widths.exceps[widths.nExceps].last = j; - widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; + GfxFontCIDWidthExcep excep { static_cast(j), static_cast(j), obj4.getNum() * 0.001 }; + widths.exceps.push_back(excep); ++j; - ++widths.nExceps; } else { error(errSyntaxError, -1, "Bad widths array in Type 0 font"); } @@ -1885,7 +1854,7 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionallookup("W2"); if (obj1.isArray()) { - excepsSize = 0; int i = 0; while (i + 1 < obj1.arrayGetLength()) { obj2 = obj1.arrayGet(i); obj3 = obj1.arrayGet(i + 1); if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) { if ((obj4 = obj1.arrayGet(i + 2), obj4.isNum()) && (obj5 = obj1.arrayGet(i + 3), obj5.isNum()) && (obj6 = obj1.arrayGet(i + 4), obj6.isNum())) { - if (widths.nExcepsV == excepsSize) { - excepsSize += 16; - widths.excepsV = (GfxFontCIDWidthExcepV *)greallocn(widths.excepsV, excepsSize, sizeof(GfxFontCIDWidthExcepV)); - } - widths.excepsV[widths.nExcepsV].first = obj2.getInt(); - widths.excepsV[widths.nExcepsV].last = obj3.getInt(); - widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; - widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; - widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; - ++widths.nExcepsV; + GfxFontCIDWidthExcepV excepV { static_cast(obj2.getInt()), static_cast(obj3.getInt()), obj4.getNum() * 0.001, obj5.getNum() * 0.001, obj6.getNum() * 0.001 }; + widths.excepsV.push_back(excepV); } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); } i += 5; } else if (obj2.isInt() && obj3.isArray()) { - if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) { - excepsSize = (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15; - widths.excepsV = (GfxFontCIDWidthExcepV *)greallocn(widths.excepsV, excepsSize, sizeof(GfxFontCIDWidthExcepV)); - } int j = obj2.getInt(); for (int k = 0; k < obj3.arrayGetLength(); k += 3) { if ((obj4 = obj3.arrayGet(k), obj4.isNum()) && (obj5 = obj3.arrayGet(k + 1), obj5.isNum()) && (obj6 = obj3.arrayGet(k + 2), obj6.isNum())) { - widths.excepsV[widths.nExcepsV].first = j; - widths.excepsV[widths.nExcepsV].last = j; - widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; - widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; - widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; + GfxFontCIDWidthExcepV excepV { static_cast(j), static_cast(j), obj4.getNum() * 0.001, obj5.getNum() * 0.001, obj6.getNum() * 0.001 }; + widths.excepsV.push_back(excepV); ++j; - ++widths.nExcepsV; } else { error(errSyntaxError, -1, "Bad widths (W2) array in Type 0 font"); } @@ -1950,26 +1902,13 @@ GfxCIDFont::GfxCIDFont(XRef *xref, const char *tagA, Ref idA, std::optionaldecRefCnt(); - } - gfree(widths.exceps); - gfree(widths.excepsV); - if (cidToGID) { - gfree(cidToGID); - } -} +GfxCIDFont::~GfxCIDFont() = default; int GfxCIDFont::getNextChar(const char *s, int len, CharCode *code, Unicode const **u, int *uLen, double *dx, double *dy, double *ox, double *oy) const { @@ -2012,9 +1951,9 @@ int GfxCIDFont::getNextChar(const char *s, int len, CharCode *code, Unicode cons h = widths.defHeight; vx = getWidth(cid) / 2; vy = widths.defVY; - if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) { + if (!widths.excepsV.empty() && cid >= widths.excepsV[0].first) { a = 0; - b = widths.nExcepsV; + b = widths.excepsV.size(); // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first while (b - a > 1) { m = (a + b) / 2; @@ -2047,7 +1986,7 @@ int GfxCIDFont::getWMode() const const CharCodeToUnicode *GfxCIDFont::getToUnicode() const { - return ctu; + return ctu.get(); } const GooString *GfxCIDFont::getCollection() const @@ -2067,7 +2006,7 @@ int GfxCIDFont::mapCodeToGID(FoFiTrueType *ff, int cmapi, Unicode unicode, bool return gid; } -int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) +std::vector GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff) { #define N_UCS_CANDIDATES 2 /* space characters */ @@ -2123,7 +2062,6 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) Unicode *humap = nullptr; Unicode *vumap = nullptr; Unicode *tumap = nullptr; - int *codeToGID = nullptr; int i; unsigned long code; int wmode; @@ -2133,21 +2071,19 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) int cmapPlatform, cmapEncoding; Ref embID; - *codeToGIDLen = 0; if (!ctu || !getCollection()) { - return nullptr; + return {}; } if (getEmbeddedFontID(&embID)) { if (getCollection()->cmp("Adobe-Identity") == 0) { - return nullptr; + return {}; } /* if this font is embedded font, * CIDToGIDMap should be embedded in PDF file * and already set. So return it. */ - *codeToGIDLen = getCIDToGIDLen(); return getCIDToGID(); } @@ -2169,7 +2105,7 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) } } if (cmap < 0) { - return nullptr; + return {}; } wmode = getWMode(); @@ -2179,14 +2115,13 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) } } const unsigned int n = 65536; - tumap = new Unicode[n]; humap = new Unicode[n * N_UCS_CANDIDATES]; memset(humap, 0, sizeof(Unicode) * n * N_UCS_CANDIDATES); if (lp->collection != nullptr) { - CharCodeToUnicode *tctu; GooString tname(lp->toUnicodeMap); - if ((tctu = CharCodeToUnicode::parseCMapFromFile(&tname, 16)) != nullptr) { + if (std::unique_ptr tctu = CharCodeToUnicode::parseCMapFromFile(&tname, 16)) { + tumap = new Unicode[n]; CharCode cid; for (cid = 0; cid < n; cid++) { int len; @@ -2200,15 +2135,14 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) tumap[cid] = 0; } } - delete tctu; } vumap = new Unicode[n]; memset(vumap, 0, sizeof(Unicode) * n); for (cmapName = lp->CMaps; *cmapName != nullptr; cmapName++) { - GooString cname(*cmapName); + const GooString cname(*cmapName); std::shared_ptr cnameCMap; - if ((cnameCMap = globalParams->getCMap(getCollection(), &cname)) != nullptr) { + if ((cnameCMap = globalParams->getCMap(*getCollection(), cname)) != nullptr) { if (cnameCMap->getWMode()) { cnameCMap->setReverseMap(vumap, n, 1); } else { @@ -2221,7 +2155,7 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) if (getCollection()->cmp("Adobe-Identity") == 0) { error(errSyntaxError, -1, "non-embedded font using identity encoding: {0:s}", name ? name->c_str() : "(null)"); } else { - error(errSyntaxError, -1, "Unknown character collection {0:t}\n", getCollection()); + error(errSyntaxError, -1, "Unknown character collection {0:t}", getCollection()); } if (ctu) { CharCode cid; @@ -2240,7 +2174,8 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) } } // map CID -> Unicode -> GID - codeToGID = (int *)gmallocn(n, sizeof(int)); + std::vector codeToGID; + codeToGID.resize(n, 0); for (code = 0; code < n; ++code) { Unicode unicode; unsigned long gid; @@ -2288,16 +2223,9 @@ int *GfxCIDFont::getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen) } codeToGID[code] = gid; } - *codeToGIDLen = n; - if (humap != nullptr) { - delete[] humap; - } - if (tumap != nullptr) { - delete[] tumap; - } - if (vumap != nullptr) { - delete[] vumap; - } + delete[] humap; + delete[] tumap; + delete[] vumap; return codeToGID; } @@ -2307,9 +2235,9 @@ double GfxCIDFont::getWidth(CID cid) const int a, b, m; w = widths.defWidth; - if (widths.nExceps > 0 && cid >= widths.exceps[0].first) { + if (!widths.exceps.empty() && cid >= widths.exceps[0].first) { a = 0; - b = widths.nExceps; + b = widths.exceps.size(); // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first while (b - a > 1) { m = (a + b) / 2; @@ -2339,7 +2267,7 @@ double GfxCIDFont::getWidth(char *s, int len) const // GfxFontDict //------------------------------------------------------------------------ -GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) +GfxFontDict::GfxFontDict(XRef *xref, const Ref fontDictRef, Dict *fontDict) { Ref r; @@ -2350,10 +2278,10 @@ GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) if (obj2.isDict()) { if (obj1.isRef()) { r = obj1.getRef(); - } else if (fontDictRef) { + } else if (fontDictRef != Ref::INVALID()) { // legal generation numbers are five digits, so we use a // 6-digit number here - r.gen = 100000 + fontDictRef->num; + r.gen = 100000 + fontDictRef.num; r.num = i; } else { // no indirect reference for this font, or for the containing diff --git a/poppler/GfxFont.h b/poppler/GfxFont.h index 7c955e3..b943f79 100644 --- a/poppler/GfxFont.h +++ b/poppler/GfxFont.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005, 2008, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2008, 2015, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Kristian Høgsberg // Copyright (C) 2007 Julien Rebetez @@ -24,7 +24,8 @@ // Copyright (C) 2015, 2018 Jason Crain // Copyright (C) 2015 Thomas Freitag // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich -// Copyright (C) 2021, 2022 Oliver Sander +// Copyright (C) 2021, 2022, 2024 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -97,11 +98,8 @@ struct GfxFontCIDWidths double defWidth; // default char width double defHeight; // default char height double defVY; // default origin position - GfxFontCIDWidthExcep *exceps; // exceptions - int nExceps; // number of valid entries in exceps - GfxFontCIDWidthExcepV * // exceptions for vertical font - excepsV; - int nExcepsV; // number of valid entries in excepsV + std::vector exceps; // exceptions + std::vector excepsV; // exceptions for vertical font }; //------------------------------------------------------------------------ @@ -126,11 +124,6 @@ class POPPLER_PRIVATE_EXPORT GfxFontLoc GfxFontLoc &operator=(const GfxFontLoc &) = delete; GfxFontLoc &operator=(GfxFontLoc &&other) noexcept; - // Set the 'path' string from a GooString on the heap. - // Ownership of the object is taken. - void setPath(GooString *pathA); - const GooString *pathAsGooString() const; - GfxFontLocType locType; GfxFontType fontType; Ref embFontID; // embedded stream obj ID @@ -206,7 +199,7 @@ class POPPLER_PRIVATE_EXPORT GfxFont bool matches(const char *tagA) const { return tag == tagA; } // Get font family name. - GooString *getFamily() const { return family; } + const GooString *getFamily() const { return family.get(); } // Get font stretch. Stretch getStretch() const { return stretch; } @@ -248,7 +241,7 @@ class POPPLER_PRIVATE_EXPORT GfxFont // Get the PostScript font name for the embedded font. Returns // NULL if there is no embedded font. - const GooString *getEmbeddedFontName() const { return embFontName; } + const GooString *getEmbeddedFontName() const { return embFontName.get(); } // Get font descriptor flags. int getFlags() const { return flags; } @@ -276,7 +269,8 @@ class POPPLER_PRIVATE_EXPORT GfxFont // Locate the font file for this font. If is not null, includes PS // printer-resident fonts. Returns std::optional without a value on failure. - std::optional locateFont(XRef *xref, PSOutputDev *ps); + // substituteFontName is passed down to the GlobalParams::findSystemFontFile/findBase14FontFile call + std::optional locateFont(XRef *xref, PSOutputDev *ps, GooString *substituteFontName = nullptr); // Read an external or embedded font file into a buffer. std::optional> readEmbFontFile(XRef *xref); @@ -305,18 +299,18 @@ class POPPLER_PRIVATE_EXPORT GfxFont static GfxFontType getFontType(XRef *xref, Dict *fontDict, Ref *embID); void readFontDescriptor(XRef *xref, Dict *fontDict); - CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits, CharCodeToUnicode *ctu); - static std::optional getExternalFont(GooString *path, bool cid); + [[nodiscard]] std::unique_ptr readToUnicodeCMap(Dict *fontDict, int nBits, std::unique_ptr ctu); + static std::optional getExternalFont(const std::string &path, bool cid); const std::string tag; // PDF font tag const Ref id; // reference (used as unique ID) std::optional name; // font name - GooString *family; // font family + std::unique_ptr family; // font family Stretch stretch; // font stretch Weight weight; // font weight const GfxFontType type; // type of font int flags; // font descriptor flags - GooString *embFontName; // name of embedded font + std::unique_ptr embFontName; // name of embedded font Ref embFontID; // ref to embedded font file stream double fontMat[6]; // font matrix (Type 3 only) double fontBBox[4]; // font bounding box (Type 3 only) @@ -359,7 +353,7 @@ class POPPLER_PRIVATE_EXPORT Gfx8BitFont : public GfxFont // Return a char code-to-GID mapping for the provided font file. // (This is only useful for TrueType fonts.) - int *getCodeToGIDMap(FoFiTrueType *ff); + std::vector getCodeToGIDMap(FoFiTrueType *ff); // Return the Type 3 CharProc dictionary, or NULL if none. Dict *getCharProcs(); @@ -371,14 +365,14 @@ class POPPLER_PRIVATE_EXPORT Gfx8BitFont : public GfxFont // Return the Type 3 Resources dictionary, or NULL if none. Dict *getResources(); -private: ~Gfx8BitFont() override; +private: const Base14FontMapEntry *base14; // for Base-14 fonts only; NULL otherwise char *enc[256]; // char code --> char name char encFree[256]; // boolean for each char name: if set, // the string is malloc'ed - CharCodeToUnicode *ctu; // char code --> Unicode + std::unique_ptr ctu; // char code --> Unicode bool hasEncoding; bool usesMacRomanEnc; double widths[256]; // character widths @@ -412,28 +406,27 @@ class POPPLER_PRIVATE_EXPORT GfxCIDFont : public GfxFont // Return the CID-to-GID mapping table. These should only be called // if type is fontCIDType2. - int *getCIDToGID() const { return cidToGID; } - unsigned int getCIDToGIDLen() const { return cidToGIDLen; } + const std::vector &getCIDToGID() const { return cidToGID; } + unsigned int getCIDToGIDLen() const { return cidToGID.size(); } - int *getCodeToGIDMap(FoFiTrueType *ff, int *codeToGIDLen); + std::vector getCodeToGIDMap(FoFiTrueType *ff); double getWidth(char *s, int len) const; -private: ~GfxCIDFont() override; +private: int mapCodeToGID(FoFiTrueType *ff, int cmapi, Unicode unicode, bool wmode); double getWidth(CID cid) const; // Get width of a character. - GooString *collection; // collection name + std::unique_ptr collection; // collection name std::shared_ptr cMap; // char code --> CID - CharCodeToUnicode *ctu; // CID --> Unicode + std::shared_ptr ctu; // CID --> Unicode bool ctuUsesCharCode; // true: ctu maps char code to Unicode; // false: ctu maps CID to Unicode GfxFontCIDWidths widths; // character widths - int *cidToGID; // CID --> GID mapping (for embedded - // TrueType fonts) - unsigned int cidToGIDLen; + std::vector cidToGID; // CID --> GID mapping (for embedded + // TrueType fonts) }; //------------------------------------------------------------------------ @@ -444,7 +437,7 @@ class GfxFontDict { public: // Build the font dictionary, given the PDF font dictionary. - GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict); + GfxFontDict(XRef *xref, const Ref fontDictRef, Dict *fontDict); GfxFontDict(const GfxFontDict &) = delete; GfxFontDict &operator=(const GfxFontDict &) = delete; diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc index 9742191..624dd3d 100644 --- a/poppler/GfxState.cc +++ b/poppler/GfxState.cc @@ -16,9 +16,9 @@ // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2006, 2010 Carlos Garcia Campos -// Copyright (C) 2006-2022 Albert Astals Cid +// Copyright (C) 2006-2022, 2024 Albert Astals Cid // Copyright (C) 2009, 2012 Koji Otani -// Copyright (C) 2009, 2011-2016, 2020 Thomas Freitag +// Copyright (C) 2009, 2011-2016, 2020, 2023 Thomas Freitag // Copyright (C) 2009, 2019 Christian Persch // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2010 Christian Feuersänger @@ -36,6 +36,9 @@ // Copyright (C) 2019 LE GARREC Vincent // Copyright (C) 2020, 2021 Philipp Knechtges // Copyright (C) 2020 Lluís Batlle i Rossell +// Copyright (C) 2024 Athul Raj Kollareth +// Copyright (C) 2024 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -157,7 +160,7 @@ static const GfxBlendModeInfo gfxBlendModeNames[] = { { "Normal", gfxBlendNormal // // NB: This must match the GfxColorSpaceMode enum defined in // GfxState.h -static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "DeviceRGB", "CalRGB", "DeviceCMYK", "Lab", "ICCBased", "Indexed", "Separation", "DeviceN", "Pattern" }; +static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "DeviceRGB", "CalRGB", "DeviceCMYK", "Lab", "ICCBased", "Indexed", "Separation", "DeviceN", "Pattern", "DeviceRGBA" }; #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) @@ -166,7 +169,7 @@ static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "Device static const std::map::size_type CMSCACHE_LIMIT = 2048; # include -# define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION +# define LCMS_FLAGS (cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION) static void lcmsprofiledeleter(void *profile) { @@ -216,55 +219,53 @@ GfxColorSpace::GfxColorSpace() mapping = nullptr; } -GfxColorSpace::~GfxColorSpace() { } +GfxColorSpace::~GfxColorSpace() = default; -GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion) { - GfxColorSpace *cs; Object obj1; if (recursion > colorSpaceRecursionLimit) { error(errSyntaxError, -1, "Loop detected in color space objects"); - return nullptr; + return {}; } - cs = nullptr; if (csObj->isName()) { if (csObj->isName("DeviceGray") || csObj->isName("G")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } } else if (csObj->isName("Pattern")) { - cs = new GfxPatternColorSpace(nullptr); + return std::make_unique(nullptr); } else { error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName()); } @@ -274,51 +275,51 @@ GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } } else if (obj1.isName("CalGray")) { - cs = GfxCalGrayColorSpace::parse(csObj->getArray(), state); + return GfxCalGrayColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("CalRGB")) { - cs = GfxCalRGBColorSpace::parse(csObj->getArray(), state); + return GfxCalRGBColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("Lab")) { - cs = GfxLabColorSpace::parse(csObj->getArray(), state); + return GfxLabColorSpace::parse(csObj->getArray(), state); } else if (obj1.isName("ICCBased")) { - cs = GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion); + return GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion); } else if (obj1.isName("Indexed") || obj1.isName("I")) { - cs = GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion); + return GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("Separation")) { - cs = GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion); + return GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("DeviceN")) { - cs = GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion); + return GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else if (obj1.isName("Pattern")) { - cs = GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion); + return GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion); } else { error(errSyntaxWarning, -1, "Bad color space"); } @@ -328,34 +329,34 @@ GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultGray"); if (objCS.isNull()) { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultGrayColorSpace(); + return state->copyDefaultGrayColorSpace(); } } else if (obj1.isName("DeviceRGB")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultRGB"); if (objCS.isNull()) { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultRGBColorSpace(); + return state->copyDefaultRGBColorSpace(); } } else if (obj1.isName("DeviceCMYK")) { if (res != nullptr) { Object objCS = res->lookupColorSpace("DefaultCMYK"); if (objCS.isNull()) { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } else { - cs = GfxColorSpace::parse(nullptr, &objCS, out, state); + return GfxColorSpace::parse(nullptr, &objCS, out, state); } } else { - cs = state->copyDefaultCMYKColorSpace(); + return state->copyDefaultCMYKColorSpace(); } } else { error(errSyntaxWarning, -1, "Bad color space dict'"); @@ -363,13 +364,10 @@ GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev } else { error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict"); } - return cs; + return {}; } -void GfxColorSpace::createMapping(std::vector *separationList, int maxSepComps) -{ - return; -} +void GfxColorSpace::createMapping(std::vector> *separationList, int maxSepComps) { } void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const { @@ -522,13 +520,13 @@ unsigned int getCMSNChannels(cmsColorSpaceSignature cs) // GfxDeviceGrayColorSpace //------------------------------------------------------------------------ -GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() { } +GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() = default; -GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() { } +GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() = default; -GfxColorSpace *GfxDeviceGrayColorSpace::copy() const +std::unique_ptr GfxDeviceGrayColorSpace::copy() const { - return new GfxDeviceGrayColorSpace(); + return std::make_unique(); } void GfxDeviceGrayColorSpace::getGray(const GfxColor *color, GfxGray *gray) const @@ -623,13 +621,11 @@ GfxCalGrayColorSpace::GfxCalGrayColorSpace() gamma = 1; } -GfxCalGrayColorSpace::~GfxCalGrayColorSpace() { } +GfxCalGrayColorSpace::~GfxCalGrayColorSpace() = default; -GfxColorSpace *GfxCalGrayColorSpace::copy() const +std::unique_ptr GfxCalGrayColorSpace::copy() const { - GfxCalGrayColorSpace *cs; - - cs = new GfxCalGrayColorSpace(); + auto cs = std::make_unique(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; @@ -717,17 +713,16 @@ static void inline bradford_transform_to_d65(double &X, double &Y, double &Z, co Z = -0.00802913 * rho_in + 0.04166125 * gamma_in + 1.05519788 * beta_in; } -GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, GfxState *state) +std::unique_ptr GfxCalGrayColorSpace::parse(Array *arr, GfxState *state) { - GfxCalGrayColorSpace *cs; Object obj1, obj2; obj1 = arr->get(1); if (!obj1.isDict()) { error(errSyntaxWarning, -1, "Bad CalGray color space"); - return nullptr; + return {}; } - cs = new GfxCalGrayColorSpace(); + auto cs = std::make_unique(); obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); @@ -877,13 +872,13 @@ void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) const // GfxDeviceRGBColorSpace //------------------------------------------------------------------------ -GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() { } +GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() = default; -GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() { } +GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() = default; -GfxColorSpace *GfxDeviceRGBColorSpace::copy() const +std::unique_ptr GfxDeviceRGBColorSpace::copy() const { - return new GfxDeviceRGBColorSpace(); + return std::make_unique(); } void GfxDeviceRGBColorSpace::getGray(const GfxColor *color, GfxGray *gray) const @@ -1022,6 +1017,32 @@ void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) const color->c[2] = 0; } +//------------------------------------------------------------------------ +// GfxDeviceRGBAColorSpace +//------------------------------------------------------------------------ + +GfxDeviceRGBAColorSpace::GfxDeviceRGBAColorSpace() = default; + +GfxDeviceRGBAColorSpace::~GfxDeviceRGBAColorSpace() = default; + +std::unique_ptr GfxDeviceRGBAColorSpace::copy() const +{ + return std::make_unique(); +} + +void GfxDeviceRGBAColorSpace::getARGBPremultipliedLine(unsigned char *in, unsigned int *out, int length) +{ + unsigned char *p; + int i; + + // Conversion from 'in' RGBA to 'out' ARGB32_PREMULTIPLIED (used by Cairo) + for (i = 0, p = in; i < length; i++, p += 4) { + // This applies alpha component p[3] to each RGB values (using bitwise division) + // so final result in out[i] is ARGB32 with premultiplied alpha. + out[i] = p[3] << 24 | (p[0] * p[3] >> 8) << 16 | (p[1] * p[3] >> 8) << 8 | (p[2] * p[3] >> 8) << 0; + } +} + //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ @@ -1042,14 +1063,13 @@ GfxCalRGBColorSpace::GfxCalRGBColorSpace() mat[8] = 1; } -GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { } +GfxCalRGBColorSpace::~GfxCalRGBColorSpace() = default; -GfxColorSpace *GfxCalRGBColorSpace::copy() const +std::unique_ptr GfxCalRGBColorSpace::copy() const { - GfxCalRGBColorSpace *cs; int i; - cs = new GfxCalRGBColorSpace(); + auto cs = std::make_unique(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; @@ -1068,18 +1088,17 @@ GfxColorSpace *GfxCalRGBColorSpace::copy() const return cs; } -GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, GfxState *state) +std::unique_ptr GfxCalRGBColorSpace::parse(Array *arr, GfxState *state) { - GfxCalRGBColorSpace *cs; Object obj1, obj2; int i; obj1 = arr->get(1); if (!obj1.isDict()) { error(errSyntaxWarning, -1, "Bad CalRGB color space"); - return nullptr; + return {}; } - cs = new GfxCalRGBColorSpace(); + auto cs = std::make_unique(); obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1); @@ -1247,13 +1266,13 @@ void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) const // GfxDeviceCMYKColorSpace //------------------------------------------------------------------------ -GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() { } +GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() = default; -GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() { } +GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() = default; -GfxColorSpace *GfxDeviceCMYKColorSpace::copy() const +std::unique_ptr GfxDeviceCMYKColorSpace::copy() const { - return new GfxDeviceCMYKColorSpace(); + return std::make_unique(); } void GfxDeviceCMYKColorSpace::getGray(const GfxColor *color, GfxGray *gray) const @@ -1389,13 +1408,11 @@ GfxLabColorSpace::GfxLabColorSpace() aMax = bMax = 100; } -GfxLabColorSpace::~GfxLabColorSpace() { } +GfxLabColorSpace::~GfxLabColorSpace() = default; -GfxColorSpace *GfxLabColorSpace::copy() const +std::unique_ptr GfxLabColorSpace::copy() const { - GfxLabColorSpace *cs; - - cs = new GfxLabColorSpace(); + auto cs = std::make_unique(); cs->whiteX = whiteX; cs->whiteY = whiteY; cs->whiteZ = whiteZ; @@ -1412,17 +1429,16 @@ GfxColorSpace *GfxLabColorSpace::copy() const return cs; } -GfxColorSpace *GfxLabColorSpace::parse(Array *arr, GfxState *state) +std::unique_ptr GfxLabColorSpace::parse(Array *arr, GfxState *state) { - GfxLabColorSpace *cs; Object obj1, obj2; obj1 = arr->get(1); if (!obj1.isDict()) { error(errSyntaxWarning, -1, "Bad Lab color space"); - return nullptr; + return {}; } - cs = new GfxLabColorSpace(); + auto cs = std::make_unique(); bool ok = true; obj2 = obj1.dictLookup("WhitePoint"); if (obj2.isArray() && obj2.arrayGetLength() == 3) { @@ -1449,8 +1465,7 @@ GfxColorSpace *GfxLabColorSpace::parse(Array *arr, GfxState *state) #ifdef USE_CMS cs->transform = nullptr; #endif - delete cs; - return nullptr; + return {}; } #ifdef USE_CMS @@ -1647,10 +1662,9 @@ void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, // GfxICCBasedColorSpace //------------------------------------------------------------------------ -GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, const Ref *iccProfileStreamA) +GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, std::unique_ptr &&altA, const Ref *iccProfileStreamA) : alt(std::move(altA)) { nComps = nCompsA; - alt = altA; iccProfileStream = *iccProfileStreamA; rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; @@ -1663,7 +1677,6 @@ GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, c GfxICCBasedColorSpace::~GfxICCBasedColorSpace() { - delete alt; #ifdef USE_CMS if (psCSA) { gfree(psCSA); @@ -1671,12 +1684,16 @@ GfxICCBasedColorSpace::~GfxICCBasedColorSpace() #endif } -GfxColorSpace *GfxICCBasedColorSpace::copy() const +std::unique_ptr GfxICCBasedColorSpace::copy() const +{ + return copyAsOwnType(); +} + +std::unique_ptr GfxICCBasedColorSpace::copyAsOwnType() const { - GfxICCBasedColorSpace *cs; int i; - cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream); + auto cs = std::make_unique(nComps, alt->copy(), &iccProfileStream); for (i = 0; i < 4; ++i) { cs->rangeMin[i] = rangeMin[i]; cs->rangeMax[i] = rangeMax[i]; @@ -1689,18 +1706,16 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() const return cs; } -GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState *state, int recursion) { - GfxICCBasedColorSpace *cs; int nCompsA; - GfxColorSpace *altA; Dict *dict; Object obj1, obj2; int i; if (arr->getLength() < 2) { error(errSyntaxError, -1, "Bad ICCBased color space"); - return nullptr; + return {}; } const Object &obj1Ref = arr->getNF(1); const Ref iccProfileStreamA = obj1Ref.isRef() ? obj1Ref.getRef() : Ref::INVALID(); @@ -1708,7 +1723,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState // check cache if (out && iccProfileStreamA != Ref::INVALID()) { if (auto *item = out->getIccColorSpaceCache()->lookup(iccProfileStreamA)) { - cs = static_cast(item->copy()); + std::unique_ptr cs = item->copyAsOwnType(); int transformIntent = cs->getIntent(); int cmsIntent = INTENT_RELATIVE_COLORIMETRIC; if (state != nullptr) { @@ -1717,7 +1732,6 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState if (transformIntent == cmsIntent) { return cs; } - delete cs; } } #endif @@ -1738,16 +1752,17 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState nCompsA = 4; } obj2 = dict->lookup("Alternate"); + std::unique_ptr altA; if (obj2.isNull() || !(altA = GfxColorSpace::parse(nullptr, &obj2, out, state, recursion + 1))) { switch (nCompsA) { case 1: - altA = new GfxDeviceGrayColorSpace(); + altA = std::make_unique(); break; case 3: - altA = new GfxDeviceRGBColorSpace(); + altA = std::make_unique(); break; case 4: - altA = new GfxDeviceCMYKColorSpace(); + altA = std::make_unique(); break; default: error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N"); @@ -1756,10 +1771,9 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState } if (altA->getNComps() != nCompsA) { error(errSyntaxWarning, -1, "Bad ICCBased color space - N doesn't match alt color space"); - delete altA; - return nullptr; + return {}; } - cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA); + auto cs = std::make_unique(nCompsA, std::move(altA), &iccProfileStreamA); obj2 = dict->lookup("Range"); if (obj2.isArray() && obj2.arrayGetLength() == 2 * nCompsA) { for (i = 0; i < nCompsA; ++i) { @@ -1772,8 +1786,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState obj1 = arr->get(1); if (!obj1.isStream()) { error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)"); - delete cs; - return nullptr; + return {}; } Stream *iccStream = obj1.getStream(); @@ -1787,7 +1800,7 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState } // put this colorSpace into cache if (out && iccProfileStreamA != Ref::INVALID()) { - out->getIccColorSpaceCache()->put(iccProfileStreamA, static_cast(cs->copy())); + out->getIccColorSpaceCache()->put(iccProfileStreamA, cs->copyAsOwnType()); } #endif return cs; @@ -2284,18 +2297,6 @@ void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRa #ifdef USE_CMS char *GfxICCBasedColorSpace::getPostScriptCSA() { -# if LCMS_VERSION >= 2070 - // The runtime version check of lcms2 is only available from release 2.7 upwards. - // The generation of the CSA code only works reliably for version 2.10 and upwards. - // Cf. the explanation in the corresponding lcms2 merge request [1], and the original mail thread [2]. - // [1] https://github.com/mm2/Little-CMS/pull/214 - // [2] https://sourceforge.net/p/lcms/mailman/message/33182987/ - if (cmsGetEncodedCMMversion() < 2100) { - return nullptr; - } - - int size; - if (psCSA) { return psCSA; } @@ -2306,7 +2307,7 @@ char *GfxICCBasedColorSpace::getPostScriptCSA() } void *rawprofile = profile.get(); - size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0); + const int size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0); if (size == 0) { error(errSyntaxWarning, -1, "PostScript CSA is nullptr"); return nullptr; @@ -2316,21 +2317,7 @@ char *GfxICCBasedColorSpace::getPostScriptCSA() cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, psCSA, size); psCSA[size] = 0; - // TODO REMOVE-ME-IN-THE-FUTURE - // until we can depend on https://github.com/mm2/Little-CMS/issues/223 being fixed - // lcms returns ps code with , instead of . for some locales. The lcms author says - // that there's no room for any , in the rest of the ps code, so replacing all the , with . - // is an "acceptable" workaround - for (int i = 0; i < size; ++i) { - if (psCSA[i] == ',') { - psCSA[i] = '.'; - } - } - return psCSA; -# else - return nullptr; -# endif } #endif @@ -2338,9 +2325,8 @@ char *GfxICCBasedColorSpace::getPostScriptCSA() // GfxIndexedColorSpace //------------------------------------------------------------------------ -GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA) +GfxIndexedColorSpace::GfxIndexedColorSpace(std::unique_ptr &&baseA, int indexHighA) : base(std::move(baseA)) { - base = baseA; indexHigh = indexHighA; lookup = (unsigned char *)gmallocn((indexHigh + 1) * base->getNComps(), sizeof(unsigned char)); overprintMask = base->getOverprintMask(); @@ -2348,22 +2334,20 @@ GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA) GfxIndexedColorSpace::~GfxIndexedColorSpace() { - delete base; gfree(lookup); } -GfxColorSpace *GfxIndexedColorSpace::copy() const +std::unique_ptr GfxIndexedColorSpace::copy() const { - GfxIndexedColorSpace *cs; - cs = new GfxIndexedColorSpace(base->copy(), indexHigh); + auto cs = std::make_unique(base->copy(), indexHigh); memcpy(cs->lookup, lookup, (indexHigh + 1) * base->getNComps() * sizeof(unsigned char)); return cs; } -GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { - GfxColorSpace *baseA; + std::unique_ptr baseA; int indexHighA; Object obj1; const char *s; @@ -2376,13 +2360,12 @@ GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, Output obj1 = arr->get(1); if (!(baseA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) { error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)"); - return nullptr; + return {}; } obj1 = arr->get(2); if (!obj1.isInt()) { error(errSyntaxWarning, -1, "Bad Indexed color space (hival)"); - delete baseA; - return nullptr; + return {}; } indexHighA = obj1.getInt(); if (indexHighA < 0 || indexHighA > 255) { @@ -2398,11 +2381,10 @@ GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, Output } error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA); } - GfxIndexedColorSpace *cs = new GfxIndexedColorSpace(baseA, indexHighA); + auto cs = std::make_unique(std::move(baseA), indexHighA); obj1 = arr->get(3); - const int n = baseA->getNComps(); - if (obj1.isStream()) { - obj1.streamReset(); + const int n = cs->getBase()->getNComps(); + if (obj1.isStream() && obj1.streamReset()) { for (i = 0; i <= indexHighA; ++i) { const int readChars = obj1.streamGetChars(n, &cs->lookup[i * n]); for (j = readChars; j < n; ++j) { @@ -2429,8 +2411,7 @@ GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, Output return cs; err3: - delete cs; - return nullptr; + return {}; } GfxColor *GfxIndexedColorSpace::mapColorToBase(const GfxColor *color, GfxColor *baseColor) const @@ -2588,10 +2569,8 @@ void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRan // GfxSeparationColorSpace //------------------------------------------------------------------------ -GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA) +GfxSeparationColorSpace::GfxSeparationColorSpace(std::unique_ptr &&nameA, std::unique_ptr &&altA, Function *funcA) : name(std::move(nameA)), alt(std::move(altA)) { - name = nameA; - alt = altA; func = funcA; nonMarking = !name->cmp("None"); if (!name->cmp("Cyan")) { @@ -2607,10 +2586,9 @@ GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace } } -GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA) +GfxSeparationColorSpace::GfxSeparationColorSpace(std::unique_ptr &&nameA, std::unique_ptr &&altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA) + : name(std::move(nameA)), alt(std::move(altA)) { - name = nameA; - alt = altA; func = funcA; nonMarking = nonMarkingA; overprintMask = overprintMaskA; @@ -2619,67 +2597,65 @@ GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, GfxColorSpace GfxSeparationColorSpace::~GfxSeparationColorSpace() { - delete name; - delete alt; delete func; if (mapping != nullptr) { gfree(mapping); } } -GfxColorSpace *GfxSeparationColorSpace::copy() const +std::unique_ptr GfxSeparationColorSpace::copy() const +{ + return copyAsOwnType(); +} + +std::unique_ptr GfxSeparationColorSpace::copyAsOwnType() const { int *mappingA = nullptr; if (mapping != nullptr) { mappingA = (int *)gmalloc(sizeof(int)); *mappingA = *mapping; } - return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(), nonMarking, overprintMask, mappingA); + auto cs = new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(), nonMarking, overprintMask, mappingA); + return std::unique_ptr(cs); } //~ handle the 'All' and 'None' colorants -GfxColorSpace *GfxSeparationColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxSeparationColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { - GooString *nameA; - GfxColorSpace *altA; + std::unique_ptr altA; Function *funcA; Object obj1; if (arr->getLength() != 4) { error(errSyntaxWarning, -1, "Bad Separation color space"); - goto err1; + return {}; } obj1 = arr->get(1); if (!obj1.isName()) { error(errSyntaxWarning, -1, "Bad Separation color space (name)"); - goto err1; + return {}; } - nameA = new GooString(obj1.getName()); + std::unique_ptr nameA = std::make_unique(obj1.getName()); obj1 = arr->get(2); if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) { error(errSyntaxWarning, -1, "Bad Separation color space (alternate color space)"); - goto err3; + return {}; } obj1 = arr->get(3); if (!(funcA = Function::parse(&obj1))) { - goto err4; + return {}; } if (funcA->getInputSize() != 1) { error(errSyntaxWarning, -1, "Bad SeparationColorSpace function"); goto err5; } if (altA->getNComps() <= funcA->getOutputSize()) { - return new GfxSeparationColorSpace(nameA, altA, funcA); + return std::make_unique(std::move(nameA), std::move(altA), funcA); } err5: delete funcA; -err4: - delete altA; -err3: - delete nameA; -err1: - return nullptr; + return {}; } void GfxSeparationColorSpace::getGray(const GfxColor *color, GfxGray *gray) const @@ -2781,7 +2757,7 @@ void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) const color->c[0] = gfxColorComp1; } -void GfxSeparationColorSpace::createMapping(std::vector *separationList, int maxSepComps) +void GfxSeparationColorSpace::createMapping(std::vector> *separationList, int maxSepComps) { if (nonMarking) { return; @@ -2803,10 +2779,10 @@ void GfxSeparationColorSpace::createMapping(std::vectorsize(); i++) { - GfxSeparationColorSpace *sepCS = (*separationList)[i]; - if (!sepCS->getName()->cmp(name)) { + const std::unique_ptr &sepCS = (*separationList)[i]; + if (!sepCS->getName()->cmp(name.get())) { if (sepCS->getFunc()->hasDifferentResultSet(func)) { - error(errSyntaxWarning, -1, "Different functions found for '{0:t}', convert immediately", name); + error(errSyntaxWarning, -1, "Different functions found for '{0:t}', convert immediately", name.get()); gfree(mapping); mapping = nullptr; return; @@ -2818,13 +2794,13 @@ void GfxSeparationColorSpace::createMapping(std::vectorsize() == maxSepComps) { - error(errSyntaxWarning, -1, "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, name); + error(errSyntaxWarning, -1, "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, name.get()); gfree(mapping); mapping = nullptr; return; } *mapping = separationList->size() + 4; - separationList->push_back((GfxSeparationColorSpace *)copy()); + separationList->push_back(copyAsOwnType()); overprintMask = newOverprintMask; break; } @@ -2834,9 +2810,9 @@ void GfxSeparationColorSpace::createMapping(std::vector &&namesA, GfxColorSpace *altA, Function *funcA, std::vector *sepsCSA) : nComps(nCompsA), names(std::move(namesA)) +GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, std::vector &&namesA, std::unique_ptr &&altA, Function *funcA, std::vector> *sepsCSA) + : nComps(nCompsA), names(std::move(namesA)), alt(std::move(altA)) { - alt = altA; func = funcA; sepsCS = sepsCSA; nonMarking = true; @@ -2856,17 +2832,16 @@ GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, std::vector overprintMask |= 0x08; } else if (names[i] == "All") { overprintMask = 0xffffffff; - } else { + } else if (names[i] != "None") { overprintMask = 0x0f; } } } -GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, const std::vector &namesA, GfxColorSpace *altA, Function *funcA, std::vector *sepsCSA, int *mappingA, bool nonMarkingA, - unsigned int overprintMaskA) - : nComps(nCompsA), names(namesA) +GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, const std::vector &namesA, std::unique_ptr &&altA, Function *funcA, std::vector> *sepsCSA, int *mappingA, + bool nonMarkingA, unsigned int overprintMaskA) + : nComps(nCompsA), names(namesA), alt(std::move(altA)) { - alt = altA; func = funcA; sepsCS = sepsCSA; mapping = mappingA; @@ -2876,26 +2851,22 @@ GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, const std::vector GfxDeviceNColorSpace::copy() const { int *mappingA = nullptr; - auto sepsCSA = new std::vector(); + auto sepsCSA = new std::vector>(); sepsCSA->reserve(sepsCS->size()); - for (const GfxSeparationColorSpace *scs : *sepsCS) { + for (const std::unique_ptr &scs : *sepsCS) { if (likely(scs != nullptr)) { - sepsCSA->push_back((GfxSeparationColorSpace *)scs->copy()); + sepsCSA->push_back(scs->copyAsOwnType()); } } if (mapping != nullptr) { @@ -2904,18 +2875,19 @@ GfxColorSpace *GfxDeviceNColorSpace::copy() const mappingA[i] = mapping[i]; } } - return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(), sepsCSA, mappingA, nonMarking, overprintMask); + auto cs = new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(), sepsCSA, mappingA, nonMarking, overprintMask); + return std::unique_ptr(cs); } //~ handle the 'None' colorant -GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { int nCompsA; std::vector namesA; - GfxColorSpace *altA; + std::unique_ptr altA; Function *funcA; Object obj1; - auto separationList = new std::vector(); + auto separationList = new std::vector>(); if (arr->getLength() != 4 && arr->getLength() != 5) { error(errSyntaxWarning, -1, "Bad DeviceN color space"); @@ -2947,7 +2919,7 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, Output } obj1 = arr->get(3); if (!(funcA = Function::parse(&obj1))) { - goto err4; + goto err1; } if (arr->getLength() == 5) { obj1 = arr->get(4); @@ -2962,9 +2934,9 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, Output for (int i = 0; i < colorants->getLength(); i++) { Object obj3 = colorants->getVal(i); if (obj3.isArray()) { - GfxSeparationColorSpace *cs = (GfxSeparationColorSpace *)GfxSeparationColorSpace::parse(res, obj3.getArray(), out, state, recursion); + auto cs = GfxSeparationColorSpace::parse(res, obj3.getArray(), out, state, recursion); if (cs) { - separationList->push_back(cs); + separationList->push_back(std::unique_ptr(static_cast(cs.release()))); } } else { error(errSyntaxWarning, -1, "Bad DeviceN color space (colorant value entry is not an Array)"); @@ -2975,13 +2947,11 @@ GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, Output } if (likely(nCompsA >= funcA->getInputSize() && altA->getNComps() <= funcA->getOutputSize())) { - return new GfxDeviceNColorSpace(nCompsA, std::move(namesA), altA, funcA, separationList); + return std::make_unique(nCompsA, std::move(namesA), std::move(altA), funcA, separationList); } err5: delete funcA; -err4: - delete altA; err1: delete separationList; return nullptr; @@ -3064,7 +3034,7 @@ void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) const } } -void GfxDeviceNColorSpace::createMapping(std::vector *separationList, int maxSepComps) +void GfxDeviceNColorSpace::createMapping(std::vector> *separationList, int maxSepComps) { if (nonMarking) { // None return; @@ -3093,7 +3063,7 @@ void GfxDeviceNColorSpace::createMapping(std::vector if (nComps == 1) { sepFunc = func; } else { - for (const GfxSeparationColorSpace *sepCS : *sepsCS) { + for (const std::unique_ptr &sepCS : *sepsCS) { if (!sepCS->getName()->cmp(names[i])) { sepFunc = sepCS->getFunc(); break; @@ -3101,7 +3071,7 @@ void GfxDeviceNColorSpace::createMapping(std::vector } } for (std::size_t j = 0; j < separationList->size(); j++) { - GfxSeparationColorSpace *sepCS = (*separationList)[j]; + const std::unique_ptr &sepCS = (*separationList)[j]; if (!sepCS->getName()->cmp(names[i])) { if (sepFunc != nullptr && sepCS->getFunc()->hasDifferentResultSet(sepFunc)) { error(errSyntaxWarning, -1, "Different functions found for '{0:s}', convert immediately", names[i].c_str()); @@ -3128,12 +3098,12 @@ void GfxDeviceNColorSpace::createMapping(std::vector mapping[i] = separationList->size() + 4; newOverprintMask |= startOverprintMask; if (nComps == 1) { - separationList->push_back(new GfxSeparationColorSpace(new GooString(names[i]), alt->copy(), func->copy())); + separationList->push_back(std::make_unique(std::make_unique(names[i]), alt->copy(), func->copy())); } else { - for (const GfxSeparationColorSpace *sepCS : *sepsCS) { + for (const std::unique_ptr &sepCS : *sepsCS) { if (!sepCS->getName()->cmp(names[i])) { found = true; - separationList->push_back((GfxSeparationColorSpace *)sepCS->copy()); + separationList->push_back(sepCS->copyAsOwnType()); break; } } @@ -3155,43 +3125,32 @@ void GfxDeviceNColorSpace::createMapping(std::vector // GfxPatternColorSpace //------------------------------------------------------------------------ -GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) -{ - under = underA; -} +GfxPatternColorSpace::GfxPatternColorSpace(std::unique_ptr &&underA) : under(std::move(underA)) { } -GfxPatternColorSpace::~GfxPatternColorSpace() -{ - if (under) { - delete under; - } -} +GfxPatternColorSpace::~GfxPatternColorSpace() = default; -GfxColorSpace *GfxPatternColorSpace::copy() const +std::unique_ptr GfxPatternColorSpace::copy() const { - return new GfxPatternColorSpace(under ? under->copy() : nullptr); + return std::make_unique(under ? under->copy() : nullptr); } -GfxColorSpace *GfxPatternColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) +std::unique_ptr GfxPatternColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) { - GfxPatternColorSpace *cs; - GfxColorSpace *underA; Object obj1; if (arr->getLength() != 1 && arr->getLength() != 2) { error(errSyntaxWarning, -1, "Bad Pattern color space"); - return nullptr; + return {}; } - underA = nullptr; + std::unique_ptr underA; if (arr->getLength() == 2) { obj1 = arr->get(1); if (!(underA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) { error(errSyntaxWarning, -1, "Bad Pattern color space (underlying color space)"); - return nullptr; + return {}; } } - cs = new GfxPatternColorSpace(underA); - return cs; + return std::make_unique(std::move(underA)); } void GfxPatternColorSpace::getGray(const GfxColor *color, GfxGray *gray) const @@ -3227,11 +3186,10 @@ void GfxPatternColorSpace::getDefaultColor(GfxColor *color) const GfxPattern::GfxPattern(int typeA, int patternRefNumA) : type(typeA), patternRefNum(patternRefNumA) { } -GfxPattern::~GfxPattern() { } +GfxPattern::~GfxPattern() = default; -GfxPattern *GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum) +std::unique_ptr GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum) { - GfxPattern *pattern; Object obj1; if (obj->isDict()) { @@ -3239,22 +3197,21 @@ GfxPattern *GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, Gf } else if (obj->isStream()) { obj1 = obj->streamGetDict()->lookup("PatternType"); } else { - return nullptr; + return {}; } - pattern = nullptr; if (obj1.isInt() && obj1.getInt() == 1) { - pattern = GfxTilingPattern::parse(obj, patternRefNum); + return GfxTilingPattern::parse(obj, patternRefNum); } else if (obj1.isInt() && obj1.getInt() == 2) { - pattern = GfxShadingPattern::parse(res, obj, out, state, patternRefNum); + return GfxShadingPattern::parse(res, obj, out, state, patternRefNum); } - return pattern; + return {}; } //------------------------------------------------------------------------ // GfxTilingPattern //------------------------------------------------------------------------ -GfxTilingPattern *GfxTilingPattern::parse(Object *patObj, int patternRefNum) +std::unique_ptr GfxTilingPattern::parse(Object *patObj, int patternRefNum) { Dict *dict; int paintTypeA, tilingTypeA; @@ -3330,7 +3287,8 @@ GfxTilingPattern *GfxTilingPattern::parse(Object *patObj, int patternRefNum) } } - return new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObj, patternRefNum); + auto pattern = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObj, patternRefNum); + return std::unique_ptr(pattern); } GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, const double *bboxA, double xStepA, double yStepA, const Object *resDictA, const double *matrixA, const Object *contentStreamA, int patternRefNumA) @@ -3352,34 +3310,34 @@ GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, const double contentStream = contentStreamA->copy(); } -GfxTilingPattern::~GfxTilingPattern() { } +GfxTilingPattern::~GfxTilingPattern() = default; -GfxPattern *GfxTilingPattern::copy() const +std::unique_ptr GfxTilingPattern::copy() const { - return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, &resDict, matrix, &contentStream, getPatternRefNum()); + auto pattern = new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, &resDict, matrix, &contentStream, getPatternRefNum()); + return std::unique_ptr(pattern); } //------------------------------------------------------------------------ // GfxShadingPattern //------------------------------------------------------------------------ -GfxShadingPattern *GfxShadingPattern::parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum) +std::unique_ptr GfxShadingPattern::parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum) { Dict *dict; - GfxShading *shadingA; double matrixA[6]; Object obj1; int i; if (!patObj->isDict()) { - return nullptr; + return {}; } dict = patObj->getDict(); obj1 = dict->lookup("Shading"); - shadingA = GfxShading::parse(res, &obj1, out, state); + std::unique_ptr shadingA = GfxShading::parse(res, &obj1, out, state); if (!shadingA) { - return nullptr; + return {}; } matrixA[0] = 1; @@ -3398,27 +3356,23 @@ GfxShadingPattern *GfxShadingPattern::parse(GfxResources *res, Object *patObj, O } } - return new GfxShadingPattern(shadingA, matrixA, patternRefNum); + auto pattern = new GfxShadingPattern(std::move(shadingA), matrixA, patternRefNum); + return std::unique_ptr(pattern); } -GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, const double *matrixA, int patternRefNumA) : GfxPattern(2, patternRefNumA) +GfxShadingPattern::GfxShadingPattern(std::unique_ptr &&shadingA, const double *matrixA, int patternRefNumA) : GfxPattern(2, patternRefNumA), shading(std::move(shadingA)) { - int i; - - shading = shadingA; - for (i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { matrix[i] = matrixA[i]; } } -GfxShadingPattern::~GfxShadingPattern() -{ - delete shading; -} +GfxShadingPattern::~GfxShadingPattern() = default; -GfxPattern *GfxShadingPattern::copy() const +std::unique_ptr GfxShadingPattern::copy() const { - return new GfxShadingPattern(shading->copy(), matrix, getPatternRefNum()); + auto pattern = new GfxShadingPattern(shading->copy(), matrix, getPatternRefNum()); + return std::unique_ptr(pattern); } //------------------------------------------------------------------------ @@ -3427,8 +3381,7 @@ GfxPattern *GfxShadingPattern::copy() const GfxShading::GfxShading(int typeA) { - type = typeA; - colorSpace = nullptr; + type = static_cast(typeA); } GfxShading::GfxShading(const GfxShading *shading) @@ -3448,16 +3401,10 @@ GfxShading::GfxShading(const GfxShading *shading) hasBBox = shading->hasBBox; } -GfxShading::~GfxShading() -{ - if (colorSpace) { - delete colorSpace; - } -} +GfxShading::~GfxShading() = default; -GfxShading *GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) +std::unique_ptr GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) { - GfxShading *shading; Dict *dict; int typeA; Object obj1; @@ -3467,67 +3414,58 @@ GfxShading *GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, Gf } else if (obj->isStream()) { dict = obj->streamGetDict(); } else { - return nullptr; + return {}; } obj1 = dict->lookup("ShadingType"); if (!obj1.isInt()) { error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary"); - return nullptr; + return {}; } typeA = obj1.getInt(); switch (typeA) { case 1: - shading = GfxFunctionShading::parse(res, dict, out, state); + return GfxFunctionShading::parse(res, dict, out, state); break; case 2: - shading = GfxAxialShading::parse(res, dict, out, state); + return GfxAxialShading::parse(res, dict, out, state); break; case 3: - shading = GfxRadialShading::parse(res, dict, out, state); + return GfxRadialShading::parse(res, dict, out, state); break; case 4: if (obj->isStream()) { - shading = GfxGouraudTriangleShading::parse(res, 4, dict, obj->getStream(), out, state); + return GfxGouraudTriangleShading::parse(res, 4, dict, obj->getStream(), out, state); } else { error(errSyntaxWarning, -1, "Invalid Type 4 shading object"); - goto err1; } break; case 5: if (obj->isStream()) { - shading = GfxGouraudTriangleShading::parse(res, 5, dict, obj->getStream(), out, state); + return GfxGouraudTriangleShading::parse(res, 5, dict, obj->getStream(), out, state); } else { error(errSyntaxWarning, -1, "Invalid Type 5 shading object"); - goto err1; } break; case 6: if (obj->isStream()) { - shading = GfxPatchMeshShading::parse(res, 6, dict, obj->getStream(), out, state); + return GfxPatchMeshShading::parse(res, 6, dict, obj->getStream(), out, state); } else { error(errSyntaxWarning, -1, "Invalid Type 6 shading object"); - goto err1; } break; case 7: if (obj->isStream()) { - shading = GfxPatchMeshShading::parse(res, 7, dict, obj->getStream(), out, state); + return GfxPatchMeshShading::parse(res, 7, dict, obj->getStream(), out, state); } else { error(errSyntaxWarning, -1, "Invalid Type 7 shading object"); - goto err1; } break; default: error(errSyntaxWarning, -1, "Unimplemented shading type {0:d}", typeA); - goto err1; } - - return shading; - -err1: - return nullptr; + return {}; } bool GfxShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) @@ -3611,11 +3549,10 @@ GfxFunctionShading::GfxFunctionShading(const GfxFunctionShading *shading) : GfxS } } -GfxFunctionShading::~GfxFunctionShading() { } +GfxFunctionShading::~GfxFunctionShading() = default; -GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) +std::unique_ptr GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { - GfxFunctionShading *shading; double x0A, y0A, x1A, y1A; double matrixA[6]; std::vector> funcsA; @@ -3634,7 +3571,7 @@ GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, Out if (!decodeOk) { error(errSyntaxWarning, -1, "Invalid Domain array in function shading dictionary"); - return nullptr; + return {}; } } @@ -3656,7 +3593,7 @@ GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, Out if (!decodeOk) { error(errSyntaxWarning, -1, "Invalid Matrix array in function shading dictionary"); - return nullptr; + return {}; } } @@ -3665,28 +3602,27 @@ GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, Out const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); - return nullptr; + return {}; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } - shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA, std::move(funcsA)); + auto shading = std::make_unique(x0A, y0A, x1A, y1A, matrixA, std::move(funcsA)); if (!shading->init(res, dict, out, state)) { - delete shading; - return nullptr; + return {}; } return shading; } @@ -3730,9 +3666,9 @@ bool GfxFunctionShading::init(GfxResources *res, Dict *dict, OutputDev *out, Gfx return true; } -GfxShading *GfxFunctionShading::copy() const +std::unique_ptr GfxFunctionShading::copy() const { - return new GfxFunctionShading(this); + return std::make_unique(this); } void GfxFunctionShading::getColor(double x, double y, GfxColor *color) const @@ -3983,11 +3919,10 @@ GfxAxialShading::GfxAxialShading(const GfxAxialShading *shading) : GfxUnivariate y1 = shading->y1; } -GfxAxialShading::~GfxAxialShading() { } +GfxAxialShading::~GfxAxialShading() = default; -GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) +std::unique_ptr GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { - GfxAxialShading *shading; double x0A, y0A, x1A, y1A; double t0A, t1A; std::vector> funcsA; @@ -4003,7 +3938,7 @@ GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev y1A = obj1.arrayGet(3).getNumWithDefaultValue(0); } else { error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary"); - return nullptr; + return {}; } t0A = 0; @@ -4019,20 +3954,20 @@ GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); - return nullptr; + return {}; } for (int i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } @@ -4054,17 +3989,16 @@ GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev } } - shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A, std::move(funcsA), extend0A, extend1A); + auto shading = std::make_unique(x0A, y0A, x1A, y1A, t0A, t1A, std::move(funcsA), extend0A, extend1A); if (!shading->init(res, dict, out, state)) { - delete shading; - shading = nullptr; + return {}; } return shading; } -GfxShading *GfxAxialShading::copy() const +std::unique_ptr GfxAxialShading::copy() const { - return new GfxAxialShading(this); + return std::make_unique(this); } double GfxAxialShading::getDistance(double sMin, double sMax) const @@ -4164,11 +4098,10 @@ GfxRadialShading::GfxRadialShading(const GfxRadialShading *shading) : GfxUnivari r1 = shading->r1; } -GfxRadialShading::~GfxRadialShading() { } +GfxRadialShading::~GfxRadialShading() = default; -GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) +std::unique_ptr GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) { - GfxRadialShading *shading; double x0A, y0A, r0A, x1A, y1A, r1A; double t0A, t1A; std::vector> funcsA; @@ -4187,7 +4120,7 @@ GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputD r1A = obj1.arrayGet(5).getNumWithDefaultValue(0); } else { error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary"); - return nullptr; + return {}; } t0A = 0; @@ -4203,20 +4136,20 @@ GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputD const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); - return nullptr; + return {}; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } @@ -4228,17 +4161,16 @@ GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputD extend1A = obj1.arrayGet(1).getBoolWithDefaultValue(false); } - shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A, std::move(funcsA), extend0A, extend1A); + auto shading = std::make_unique(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A, std::move(funcsA), extend0A, extend1A); if (!shading->init(res, dict, out, state)) { - delete shading; - return nullptr; + return {}; } return shading; } -GfxShading *GfxRadialShading::copy() const +std::unique_ptr GfxRadialShading::copy() const { - return new GfxRadialShading(this); + return std::make_unique(this); } double GfxRadialShading::getDistance(double sMin, double sMax) const @@ -4569,7 +4501,7 @@ class GfxShadingBitBuf GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) { str = strA; - str->reset(); + (void)str->reset(); bitBuf = 0; nBits = 0; } @@ -4650,9 +4582,8 @@ GfxGouraudTriangleShading::~GfxGouraudTriangleShading() gfree(triangles); } -GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *gfxState) +std::unique_ptr GfxGouraudTriangleShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *gfxState) { - GfxGouraudTriangleShading *shading; std::vector> funcsA; int coordBits, compBits, flagBits, vertsPerRow, nRows; double xMin, xMax, yMin, yMax; @@ -4673,22 +4604,22 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i coordBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); - return nullptr; + return {}; } if (unlikely(coordBits <= 0)) { error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("BitsPerComponent"); if (obj1.isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary"); - return nullptr; + return {}; } if (unlikely(compBits <= 0 || compBits > 31)) { error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary"); - return nullptr; + return {}; } flagBits = vertsPerRow = 0; // make gcc happy if (typeA == 4) { @@ -4697,7 +4628,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i flagBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary"); - return nullptr; + return {}; } } else { obj1 = dict->lookup("VerticesPerRow"); @@ -4705,7 +4636,7 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i vertsPerRow = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary"); - return nullptr; + return {}; } } obj1 = dict->lookup("Decode"); @@ -4726,11 +4657,11 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i if (!decodeOk) { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); - return nullptr; + return {}; } } else { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("Function"); @@ -4739,20 +4670,20 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); - return nullptr; + return {}; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } @@ -4853,10 +4784,9 @@ GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, i } } - shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA, trianglesA, nTrianglesA, std::move(funcsA)); + auto shading = std::make_unique(typeA, verticesA, nVerticesA, trianglesA, nTrianglesA, std::move(funcsA)); if (!shading->init(res, dict, out, gfxState)) { - delete shading; - return nullptr; + return {}; } return shading; } @@ -4901,9 +4831,9 @@ bool GfxGouraudTriangleShading::init(GfxResources *res, Dict *dict, OutputDev *o return true; } -GfxShading *GfxGouraudTriangleShading::copy() const +std::unique_ptr GfxGouraudTriangleShading::copy() const { - return new GfxGouraudTriangleShading(this); + return std::make_unique(this); } void GfxGouraudTriangleShading::getTriangle(int i, double *x0, double *y0, GfxColor *color0, double *x1, double *y1, GfxColor *color1, double *x2, double *y2, GfxColor *color2) @@ -4989,9 +4919,8 @@ GfxPatchMeshShading::~GfxPatchMeshShading() gfree(patches); } -GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state) +std::unique_ptr GfxPatchMeshShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state) { - GfxPatchMeshShading *shading; std::vector> funcsA; int coordBits, compBits, flagBits; double xMin, xMax, yMin, yMax; @@ -5013,29 +4942,29 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di coordBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary"); - return nullptr; + return {}; } if (unlikely(coordBits <= 0)) { error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("BitsPerComponent"); if (obj1.isInt()) { compBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary"); - return nullptr; + return {}; } if (unlikely(compBits <= 0 || compBits > 31)) { error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("BitsPerFlag"); if (obj1.isInt()) { flagBits = obj1.getInt(); } else { error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("Decode"); if (obj1.isArray() && obj1.arrayGetLength() >= 6) { @@ -5055,11 +4984,11 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di if (!decodeOk) { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); - return nullptr; + return {}; } } else { error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary"); - return nullptr; + return {}; } obj1 = dict->lookup("Function"); @@ -5068,20 +4997,20 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di const int nFuncsA = obj1.arrayGetLength(); if (nFuncsA > gfxColorMaxComps) { error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary"); - return nullptr; + return {}; } for (i = 0; i < nFuncsA; ++i) { Object obj2 = obj1.arrayGet(i); Function *f = Function::parse(&obj2); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } } else { Function *f = Function::parse(&obj1); if (!f) { - return nullptr; + return {}; } funcsA.emplace_back(f); } @@ -5158,7 +5087,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize; patchesA = (GfxPatch *)greallocn_checkoverflow(patchesA, patchesSize, sizeof(GfxPatch)); if (unlikely(!patchesA)) { - return nullptr; + return {}; } memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch)); } @@ -5236,7 +5165,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di case 2: if (nPatchesA == 0) { gfree(patchesA); - return nullptr; + return {}; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][3]; @@ -5272,7 +5201,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di case 3: if (nPatchesA == 0) { gfree(patchesA); - return nullptr; + return {}; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][0]; @@ -5351,7 +5280,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di case 1: if (nPatchesA == 0) { gfree(patchesA); - return nullptr; + return {}; } p->x[0][0] = patchesA[nPatchesA - 1].x[0][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[0][3]; @@ -5395,7 +5324,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di case 2: if (nPatchesA == 0) { gfree(patchesA); - return nullptr; + return {}; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][3]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][3]; @@ -5439,7 +5368,7 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di case 3: if (nPatchesA == 0) { gfree(patchesA); - return nullptr; + return {}; } p->x[0][0] = patchesA[nPatchesA - 1].x[3][0]; p->y[0][0] = patchesA[nPatchesA - 1].y[3][0]; @@ -5500,10 +5429,9 @@ GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Di } } - shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA, std::move(funcsA)); + auto shading = std::make_unique(typeA, patchesA, nPatchesA, std::move(funcsA)); if (!shading->init(res, dict, out, state)) { - delete shading; - return nullptr; + return {}; } return shading; } @@ -5560,19 +5488,17 @@ void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) const } } -GfxShading *GfxPatchMeshShading::copy() const +std::unique_ptr GfxPatchMeshShading::copy() const { - return new GfxPatchMeshShading(this); + return std::make_unique(this); } //------------------------------------------------------------------------ // GfxImageColorMap //------------------------------------------------------------------------ -GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA) +GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, std::unique_ptr &&colorSpaceA) : colorSpace(std::move(colorSpaceA)) { - GfxIndexedColorSpace *indexedCS; - GfxSeparationColorSpace *sepCS; int maxPixel, indexHigh; unsigned char *indexedLookup; const Function *sepFunc; @@ -5585,8 +5511,6 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *col ok = true; useMatte = false; - colorSpace = colorSpaceA; - // initialize for (k = 0; k < gfxColorMaxComps; ++k) { lookup[k] = nullptr; @@ -5656,11 +5580,11 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *col nComps2 = 0; useByteLookup = false; switch (colorSpace->getMode()) { - case csIndexed: + case csIndexed: { // Note that indexHigh may not be the same as maxPixel -- // Distiller will remove unused palette entries, resulting in // indexHigh < maxPixel. - indexedCS = (GfxIndexedColorSpace *)colorSpace; + GfxIndexedColorSpace *indexedCS = (GfxIndexedColorSpace *)colorSpace.get(); colorSpace2 = indexedCS->getBase(); indexHigh = indexedCS->getIndexHigh(); nComps2 = colorSpace2->getNComps(); @@ -5688,8 +5612,9 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *col } } break; - case csSeparation: - sepCS = (GfxSeparationColorSpace *)colorSpace; + } + case csSeparation: { + GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)colorSpace.get(); colorSpace2 = sepCS->getAlt(); nComps2 = colorSpace2->getNComps(); sepFunc = sepCS->getFunc(); @@ -5709,6 +5634,7 @@ GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *col } } break; + } default: if ((!decode->isNull() || maxPixel != 255) && (colorSpace->useGetGrayLine() || (colorSpace->useGetRGBLine() && !decode->isNull()) || colorSpace->useGetCMYKLine() || colorSpace->useGetDeviceNLine())) { byte_lookup = (unsigned char *)gmallocn((maxPixel + 1), nComps); @@ -5762,13 +5688,13 @@ GfxImageColorMap::GfxImageColorMap(const GfxImageColorMap *colorMap) memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp)); } if (colorSpace->getMode() == csIndexed) { - colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase(); + colorSpace2 = ((GfxIndexedColorSpace *)colorSpace.get())->getBase(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); } } else if (colorSpace->getMode() == csSeparation) { - colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt(); + colorSpace2 = ((GfxSeparationColorSpace *)colorSpace.get())->getAlt(); for (k = 0; k < nComps2; ++k) { lookup2[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp)); memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp)); @@ -5796,7 +5722,6 @@ GfxImageColorMap::~GfxImageColorMap() { int i; - delete colorSpace; for (i = 0; i < gfxColorMaxComps; ++i) { gfree(lookup[i]); gfree(lookup2[i]); @@ -6294,7 +6219,7 @@ GfxPath::~GfxPath() for (i = 0; i < n; ++i) { delete subpaths[i]; } - gfree(subpaths); + gfree(static_cast(subpaths)); } // Used for copy(). @@ -6325,7 +6250,7 @@ void GfxPath::lineTo(double x, double y) if (justMoved || (n > 0 && subpaths[n - 1]->isClosed())) { if (n >= size) { size *= 2; - subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); + subpaths = (GfxSubpath **)greallocn(static_cast(subpaths), size, sizeof(GfxSubpath *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); @@ -6343,7 +6268,7 @@ void GfxPath::curveTo(double x1, double y1, double x2, double y2, double x3, dou if (justMoved || (n > 0 && subpaths[n - 1]->isClosed())) { if (n >= size) { size *= 2; - subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); + subpaths = (GfxSubpath **)greallocn(static_cast(subpaths), size, sizeof(GfxSubpath *)); } if (justMoved) { subpaths[n] = new GfxSubpath(firstX, firstY); @@ -6363,7 +6288,7 @@ void GfxPath::close() if (justMoved) { if (n >= size) { size *= 2; - subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); + subpaths = (GfxSubpath **)greallocn(static_cast(subpaths), size, sizeof(GfxSubpath *)); } subpaths[n] = new GfxSubpath(firstX, firstY); ++n; @@ -6378,7 +6303,7 @@ void GfxPath::append(GfxPath *path) if (n + path->n > size) { size = n + path->n; - subpaths = (GfxSubpath **)greallocn(subpaths, size, sizeof(GfxSubpath *)); + subpaths = (GfxSubpath **)greallocn(static_cast(subpaths), size, sizeof(GfxSubpath *)); } for (i = 0; i < path->n; ++i) { subpaths[n++] = path->subpaths[i]->copy(); @@ -6489,8 +6414,8 @@ GfxState::GfxState(double hDPIA, double vDPIA, const PDFRectangle *pageBox, int pageHeight = ky * (py2 - py1); } - fillColorSpace = new GfxDeviceGrayColorSpace(); - strokeColorSpace = new GfxDeviceGrayColorSpace(); + fillColorSpace = std::make_unique(); + strokeColorSpace = std::make_unique(); fillColor.c[0] = 0; strokeColor.c[0] = 0; fillPattern = nullptr; @@ -6506,8 +6431,8 @@ GfxState::GfxState(double hDPIA, double vDPIA, const PDFRectangle *pageBox, int lineWidth = 1; lineDashStart = 0; flatness = 1; - lineJoin = 0; - lineCap = 0; + lineJoin = GfxState::LineJoinMitre; + lineCap = GfxState::LineCapButt; miterLimit = 10; strokeAdjust = false; alphaIsShape = false; @@ -6568,31 +6493,12 @@ GfxState::~GfxState() { int i; - if (fillColorSpace) { - delete fillColorSpace; - } - if (strokeColorSpace) { - delete strokeColorSpace; - } - if (fillPattern) { - delete fillPattern; - } - if (strokePattern) { - delete strokePattern; - } for (i = 0; i < 4; ++i) { if (transfer[i]) { delete transfer[i]; } } - if (path) { - // this gets set to NULL by restore() - delete path; - } - - delete defaultGrayColorSpace; - delete defaultRGBColorSpace; - delete defaultCMYKColorSpace; + delete path; } // Used for copy(); @@ -6611,23 +6517,19 @@ GfxState::GfxState(const GfxState *state, bool copyPath) pageHeight = state->pageHeight; rotate = state->rotate; - fillColorSpace = state->fillColorSpace; - if (fillColorSpace) { + if (state->fillColorSpace) { fillColorSpace = state->fillColorSpace->copy(); } - strokeColorSpace = state->strokeColorSpace; - if (strokeColorSpace) { + if (state->strokeColorSpace) { strokeColorSpace = state->strokeColorSpace->copy(); } fillColor = state->fillColor; strokeColor = state->strokeColor; - fillPattern = state->fillPattern; - if (fillPattern) { + if (state->fillPattern) { fillPattern = state->fillPattern->copy(); } - strokePattern = state->strokePattern; - if (strokePattern) { + if (state->strokePattern) { strokePattern = state->strokePattern->copy(); } blendMode = state->blendMode; @@ -6915,36 +6817,24 @@ void GfxState::shiftCTMAndClip(double tx, double ty) clipYMax += ty; } -void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) +void GfxState::setFillColorSpace(std::unique_ptr &&colorSpace) { - if (fillColorSpace) { - delete fillColorSpace; - } - fillColorSpace = colorSpace; + fillColorSpace = std::move(colorSpace); } -void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) +void GfxState::setStrokeColorSpace(std::unique_ptr &&colorSpace) { - if (strokeColorSpace) { - delete strokeColorSpace; - } - strokeColorSpace = colorSpace; + strokeColorSpace = std::move(colorSpace); } -void GfxState::setFillPattern(GfxPattern *pattern) +void GfxState::setFillPattern(std::unique_ptr &&pattern) { - if (fillPattern) { - delete fillPattern; - } - fillPattern = pattern; + fillPattern = std::move(pattern); } -void GfxState::setStrokePattern(GfxPattern *pattern) +void GfxState::setStrokePattern(std::unique_ptr &&pattern) { - if (strokePattern) { - delete strokePattern; - } - strokePattern = pattern; + strokePattern = std::move(pattern); } void GfxState::setFont(std::shared_ptr fontA, double fontSizeA) diff --git a/poppler/GfxState.h b/poppler/GfxState.h index cdbca9f..6e988fd 100644 --- a/poppler/GfxState.h +++ b/poppler/GfxState.h @@ -17,7 +17,7 @@ // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2006 Carlos Garcia Campos // Copyright (C) 2009 Koji Otani -// Copyright (C) 2009-2011, 2013, 2016-2022 Albert Astals Cid +// Copyright (C) 2009-2011, 2013, 2016-2022, 2024 Albert Astals Cid // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2011 Andrea Canciani // Copyright (C) 2011-2014, 2016, 2020 Thomas Freitag @@ -26,6 +26,8 @@ // Copyright (C) 2017, 2019, 2022 Oliver Sander // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020, 2021 Philipp Knechtges +// Copyright (C) 2024 Athul Raj Kollareth +// Copyright (C) 2024 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -211,7 +213,8 @@ enum GfxColorSpaceMode csIndexed, csSeparation, csDeviceN, - csPattern + csPattern, + csDeviceRGBA // used for transparent JPX images, they contain RGBA data · Issue #1486 }; // This shall hold a cmsHPROFILE handle. @@ -238,7 +241,7 @@ class GfxColorTransform int getTransformPixelType() const { return transformPixelType; } private: - GfxColorTransform() { } + GfxColorTransform() = default; void *transform; int cmsIntent; unsigned int inputPixelType; @@ -254,11 +257,11 @@ class POPPLER_PRIVATE_EXPORT GfxColorSpace GfxColorSpace(const GfxColorSpace &) = delete; GfxColorSpace &operator=(const GfxColorSpace &other) = delete; - virtual GfxColorSpace *copy() const = 0; + virtual std::unique_ptr copy() const = 0; virtual GfxColorSpaceMode getMode() const = 0; // Construct a color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion = 0); + static std::unique_ptr parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion = 0); // Convert to gray, RGB, or CMYK. virtual void getGray(const GfxColor *color, GfxGray *gray) const = 0; @@ -273,7 +276,7 @@ class POPPLER_PRIVATE_EXPORT GfxColorSpace virtual void getDeviceNLine(unsigned char * /*in*/, unsigned char * /*out*/, int /*length*/) { error(errInternal, -1, "GfxColorSpace::getDeviceNLine this should not happen"); } // create mapping for spot colorants - virtual void createMapping(std::vector *separationList, int maxSepComps); + virtual void createMapping(std::vector> *separationList, int maxSepComps); int *getMapping() const { return mapping; } // Does this ColorSpace support getRGBLine? @@ -322,7 +325,7 @@ class POPPLER_PRIVATE_EXPORT GfxDeviceGrayColorSpace : public GfxColorSpace public: GfxDeviceGrayColorSpace(); ~GfxDeviceGrayColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceGray; } void getGray(const GfxColor *color, GfxGray *gray) const override; @@ -356,11 +359,11 @@ class GfxCalGrayColorSpace : public GfxColorSpace public: GfxCalGrayColorSpace(); ~GfxCalGrayColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csCalGray; } // Construct a CalGray color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(Array *arr, GfxState *state); + static std::unique_ptr parse(Array *arr, GfxState *state); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -398,7 +401,7 @@ class POPPLER_PRIVATE_EXPORT GfxDeviceRGBColorSpace : public GfxColorSpace public: GfxDeviceRGBColorSpace(); ~GfxDeviceRGBColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceRGB; } void getGray(const GfxColor *color, GfxGray *gray) const override; @@ -423,6 +426,26 @@ class POPPLER_PRIVATE_EXPORT GfxDeviceRGBColorSpace : public GfxColorSpace private: }; +//------------------------------------------------------------------------ +// GfxDeviceRGBAColorSpace +//------------------------------------------------------------------------ + +class POPPLER_PRIVATE_EXPORT GfxDeviceRGBAColorSpace : public GfxDeviceRGBColorSpace +{ +public: + GfxDeviceRGBAColorSpace(); + ~GfxDeviceRGBAColorSpace() override; + std::unique_ptr copy() const override; + GfxColorSpaceMode getMode() const override { return csDeviceRGBA; } + + int getNComps() const override { return 4; } + + // GfxDeviceRGBAColorSpace-specific access + void getARGBPremultipliedLine(unsigned char *in, unsigned int *out, int length); + +private: +}; + //------------------------------------------------------------------------ // GfxCalRGBColorSpace //------------------------------------------------------------------------ @@ -432,11 +455,11 @@ class GfxCalRGBColorSpace : public GfxColorSpace public: GfxCalRGBColorSpace(); ~GfxCalRGBColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csCalRGB; } // Construct a CalRGB color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(Array *arr, GfxState *state); + static std::unique_ptr parse(Array *arr, GfxState *state); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -478,7 +501,7 @@ class POPPLER_PRIVATE_EXPORT GfxDeviceCMYKColorSpace : public GfxColorSpace public: GfxDeviceCMYKColorSpace(); ~GfxDeviceCMYKColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceCMYK; } void getGray(const GfxColor *color, GfxGray *gray) const override; @@ -509,11 +532,11 @@ class GfxLabColorSpace : public GfxColorSpace public: GfxLabColorSpace(); ~GfxLabColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csLab; } // Construct a Lab color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(Array *arr, GfxState *state); + static std::unique_ptr parse(Array *arr, GfxState *state); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -554,13 +577,15 @@ class GfxLabColorSpace : public GfxColorSpace class POPPLER_PRIVATE_EXPORT GfxICCBasedColorSpace : public GfxColorSpace { public: - GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, const Ref *iccProfileStreamA); + GfxICCBasedColorSpace(int nCompsA, std::unique_ptr &&altA, const Ref *iccProfileStreamA); ~GfxICCBasedColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csICCBased; } + std::unique_ptr copyAsOwnType() const; + // Construct an ICCBased color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(Array *arr, OutputDev *out, GfxState *state, int recursion); + static std::unique_ptr parse(Array *arr, OutputDev *out, GfxState *state, int recursion); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -582,34 +607,25 @@ class POPPLER_PRIVATE_EXPORT GfxICCBasedColorSpace : public GfxColorSpace void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const override; // ICCBased-specific access. - GfxColorSpace *getAlt() { return alt; } + GfxColorSpace *getAlt() { return alt.get(); } Ref getRef() { return iccProfileStream; } #ifdef USE_CMS char *getPostScriptCSA(); void buildTransforms(GfxState *state); - void setProfile(GfxLCMSProfilePtr &profileA) - { - profile = profileA; - } - GfxLCMSProfilePtr getProfile() - { - return profile; - } + void setProfile(GfxLCMSProfilePtr &profileA) { profile = profileA; } + GfxLCMSProfilePtr getProfile() { return profile; } #endif private: int nComps; // number of color components (1, 3, or 4) - GfxColorSpace *alt; // alternate color space + std::unique_ptr alt; // alternate color space double rangeMin[4]; // min values for each component double rangeMax[4]; // max values for each component Ref iccProfileStream; // the ICC profile #ifdef USE_CMS GfxLCMSProfilePtr profile; char *psCSA; - int getIntent() - { - return (transform != nullptr) ? transform->getIntent() : 0; - } + int getIntent() { return (transform != nullptr) ? transform->getIntent() : 0; } std::shared_ptr transform; std::shared_ptr lineTransform; // color transform for line mutable std::map cmsCache; @@ -622,13 +638,13 @@ class POPPLER_PRIVATE_EXPORT GfxICCBasedColorSpace : public GfxColorSpace class GfxIndexedColorSpace : public GfxColorSpace { public: - GfxIndexedColorSpace(GfxColorSpace *baseA, int indexHighA); + GfxIndexedColorSpace(std::unique_ptr &&baseA, int indexHighA); ~GfxIndexedColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csIndexed; } // Construct an Indexed color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); + static std::unique_ptr parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -650,15 +666,15 @@ class GfxIndexedColorSpace : public GfxColorSpace void getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const override; // Indexed-specific access. - GfxColorSpace *getBase() { return base; } + GfxColorSpace *getBase() { return base.get(); } int getIndexHigh() const { return indexHigh; } unsigned char *getLookup() { return lookup; } GfxColor *mapColorToBase(const GfxColor *color, GfxColor *baseColor) const; unsigned int getOverprintMask() const { return base->getOverprintMask(); } - void createMapping(std::vector *separationList, int maxSepComps) override { base->createMapping(separationList, maxSepComps); } + void createMapping(std::vector> *separationList, int maxSepComps) override { base->createMapping(separationList, maxSepComps); } private: - GfxColorSpace *base; // base color space + std::unique_ptr base; // base color space int indexHigh; // max pixel value unsigned char *lookup; // lookup table }; @@ -670,20 +686,22 @@ class GfxIndexedColorSpace : public GfxColorSpace class GfxSeparationColorSpace : public GfxColorSpace { public: - GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA); + GfxSeparationColorSpace(std::unique_ptr &&nameA, std::unique_ptr &&altA, Function *funcA); ~GfxSeparationColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csSeparation; } + std::unique_ptr copyAsOwnType() const; + // Construct a Separation color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); + static std::unique_ptr parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; void getCMYK(const GfxColor *color, GfxCMYK *cmyk) const override; void getDeviceN(const GfxColor *color, GfxColor *deviceN) const override; - void createMapping(std::vector *separationList, int maxSepComps) override; + void createMapping(std::vector> *separationList, int maxSepComps) override; int getNComps() const override { return 1; } void getDefaultColor(GfxColor *color) const override; @@ -691,15 +709,15 @@ class GfxSeparationColorSpace : public GfxColorSpace bool isNonMarking() const override { return nonMarking; } // Separation-specific access. - const GooString *getName() const { return name; } - GfxColorSpace *getAlt() { return alt; } + const GooString *getName() const { return name.get(); } + GfxColorSpace *getAlt() { return alt.get(); } const Function *getFunc() const { return func; } private: - GfxSeparationColorSpace(GooString *nameA, GfxColorSpace *altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA); + GfxSeparationColorSpace(std::unique_ptr &&nameA, std::unique_ptr &&altA, Function *funcA, bool nonMarkingA, unsigned int overprintMaskA, int *mappingA); - GooString *name; // colorant name - GfxColorSpace *alt; // alternate color space + const std::unique_ptr name; // colorant name + const std::unique_ptr alt; // alternate color space Function *func; // tint transform (into alternate color space) bool nonMarking; }; @@ -711,20 +729,20 @@ class GfxSeparationColorSpace : public GfxColorSpace class GfxDeviceNColorSpace : public GfxColorSpace { public: - GfxDeviceNColorSpace(int nCompsA, std::vector &&namesA, GfxColorSpace *alt, Function *func, std::vector *sepsCS); + GfxDeviceNColorSpace(int nCompsA, std::vector &&namesA, std::unique_ptr &&alt, Function *func, std::vector> *sepsCS); ~GfxDeviceNColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csDeviceN; } // Construct a DeviceN color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); + static std::unique_ptr parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; void getCMYK(const GfxColor *color, GfxCMYK *cmyk) const override; void getDeviceN(const GfxColor *color, GfxColor *deviceN) const override; - void createMapping(std::vector *separationList, int maxSepComps) override; + void createMapping(std::vector> *separationList, int maxSepComps) override; int getNComps() const override { return nComps; } void getDefaultColor(GfxColor *color) const override; @@ -733,18 +751,19 @@ class GfxDeviceNColorSpace : public GfxColorSpace // DeviceN-specific access. const std::string &getColorantName(int i) const { return names[i]; } - GfxColorSpace *getAlt() { return alt; } + GfxColorSpace *getAlt() { return alt.get(); } const Function *getTintTransformFunc() const { return func; } private: - GfxDeviceNColorSpace(int nCompsA, const std::vector &namesA, GfxColorSpace *alt, Function *func, std::vector *sepsCSA, int *mappingA, bool nonMarkingA, unsigned int overprintMaskA); + GfxDeviceNColorSpace(int nCompsA, const std::vector &namesA, std::unique_ptr &&alt, Function *func, std::vector> *sepsCSA, int *mappingA, bool nonMarkingA, + unsigned int overprintMaskA); const int nComps; // number of components const std::vector names; // colorant names - GfxColorSpace *alt; // alternate color space + std::unique_ptr alt; // alternate color space Function *func; // tint transform (into alternate color space) bool nonMarking; - std::vector *sepsCS; // list of separation cs for spot colorants; + std::vector> *sepsCS; // list of separation cs for spot colorants; }; //------------------------------------------------------------------------ @@ -754,13 +773,13 @@ class GfxDeviceNColorSpace : public GfxColorSpace class GfxPatternColorSpace : public GfxColorSpace { public: - explicit GfxPatternColorSpace(GfxColorSpace *underA); + explicit GfxPatternColorSpace(std::unique_ptr &&underA); ~GfxPatternColorSpace() override; - GfxColorSpace *copy() const override; + std::unique_ptr copy() const override; GfxColorSpaceMode getMode() const override { return csPattern; } // Construct a Pattern color space. Returns nullptr if unsuccessful. - static GfxColorSpace *parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); + static std::unique_ptr parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion); void getGray(const GfxColor *color, GfxGray *gray) const override; void getRGB(const GfxColor *color, GfxRGB *rgb) const override; @@ -771,11 +790,10 @@ class GfxPatternColorSpace : public GfxColorSpace void getDefaultColor(GfxColor *color) const override; // Pattern-specific access. - GfxColorSpace *getUnder() { return under; } + GfxColorSpace *getUnder() { return under.get(); } private: - GfxColorSpace *under; // underlying color space (for uncolored - // patterns) + std::unique_ptr under; // underlying color space (for uncolored patterns) }; //------------------------------------------------------------------------ @@ -791,9 +809,9 @@ class GfxPattern GfxPattern(const GfxPattern &) = delete; GfxPattern &operator=(const GfxPattern &other) = delete; - static GfxPattern *parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum); + static std::unique_ptr parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum); - virtual GfxPattern *copy() const = 0; + virtual std::unique_ptr copy() const = 0; int getType() const { return type; } @@ -811,10 +829,10 @@ class GfxPattern class GfxTilingPattern : public GfxPattern { public: - static GfxTilingPattern *parse(Object *patObj, int patternRefNum); + static std::unique_ptr parse(Object *patObj, int patternRefNum); ~GfxTilingPattern() override; - GfxPattern *copy() const override; + std::unique_ptr copy() const override; int getPaintType() const { return paintType; } int getTilingType() const { return tilingType; } @@ -844,18 +862,18 @@ class GfxTilingPattern : public GfxPattern class GfxShadingPattern : public GfxPattern { public: - static GfxShadingPattern *parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum); + static std::unique_ptr parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum); ~GfxShadingPattern() override; - GfxPattern *copy() const override; + std::unique_ptr copy() const override; - GfxShading *getShading() { return shading; } + GfxShading *getShading() { return shading.get(); } const double *getMatrix() const { return matrix; } private: - GfxShadingPattern(GfxShading *shadingA, const double *matrixA, int patternRefNumA); + GfxShadingPattern(std::unique_ptr &&shadingA, const double *matrixA, int patternRefNumA); - GfxShading *shading; + std::unique_ptr shading; double matrix[6]; }; @@ -866,6 +884,17 @@ class GfxShadingPattern : public GfxPattern class POPPLER_PRIVATE_EXPORT GfxShading { public: + enum ShadingType + { + FunctionBasedShading = 1, + AxialShading, + RadialShading, + FreeFormGouraudShadedTriangleMesh, + LatticeFormGouraudShadedTriangleMesh, + CoonsPatchMesh, + TensorProductPatchMesh + }; + explicit GfxShading(int typeA); explicit GfxShading(const GfxShading *shading); virtual ~GfxShading(); @@ -873,12 +902,12 @@ class POPPLER_PRIVATE_EXPORT GfxShading GfxShading(const GfxShading &) = delete; GfxShading &operator=(const GfxShading &other) = delete; - static GfxShading *parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state); - virtual GfxShading *copy() const = 0; + virtual std::unique_ptr copy() const = 0; - int getType() const { return type; } - GfxColorSpace *getColorSpace() { return colorSpace; } + ShadingType getType() const { return type; } + GfxColorSpace *getColorSpace() { return colorSpace.get(); } const GfxColor *getBackground() const { return &background; } bool getHasBackground() const { return hasBackground; } void getBBox(double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const @@ -893,17 +922,10 @@ class POPPLER_PRIVATE_EXPORT GfxShading protected: virtual bool init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); - // 1: Function-based shading - // 2: Axial shading - // 3: Radial shading - // 4: Free-form Gouraud-shaded triangle mesh - // 5: Lattice-form Gouraud-shaded triangle mesh - // 6: Coons patch mesh - // 7: Tensor-product patch mesh - int type; + ShadingType type; bool hasBackground; bool hasBBox; - GfxColorSpace *colorSpace; + std::unique_ptr colorSpace; GfxColor background; double bbox_xMin, bbox_yMin, bbox_xMax, bbox_yMax; }; @@ -960,9 +982,9 @@ class POPPLER_PRIVATE_EXPORT GfxFunctionShading : public GfxShading explicit GfxFunctionShading(const GfxFunctionShading *shading); ~GfxFunctionShading() override; - static GfxFunctionShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); - GfxShading *copy() const override; + std::unique_ptr copy() const override; void getDomain(double *x0A, double *y0A, double *x1A, double *y1A) const { @@ -996,9 +1018,9 @@ class GfxAxialShading : public GfxUnivariateShading explicit GfxAxialShading(const GfxAxialShading *shading); ~GfxAxialShading() override; - static GfxAxialShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); - GfxShading *copy() const override; + std::unique_ptr copy() const override; void getCoords(double *x0A, double *y0A, double *x1A, double *y1A) const { @@ -1027,9 +1049,9 @@ class GfxRadialShading : public GfxUnivariateShading explicit GfxRadialShading(const GfxRadialShading *shading); ~GfxRadialShading() override; - static GfxRadialShading *parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state); - GfxShading *copy() const override; + std::unique_ptr copy() const override; void getCoords(double *x0A, double *y0A, double *r0A, double *x1A, double *y1A, double *r1A) const { @@ -1066,9 +1088,9 @@ class POPPLER_PRIVATE_EXPORT GfxGouraudTriangleShading : public GfxShading explicit GfxGouraudTriangleShading(const GfxGouraudTriangleShading *shading); ~GfxGouraudTriangleShading() override; - static GfxGouraudTriangleShading *parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); - GfxShading *copy() const override; + std::unique_ptr copy() const override; int getNTriangles() const { return nTriangles; } @@ -1158,9 +1180,9 @@ class POPPLER_PRIVATE_EXPORT GfxPatchMeshShading : public GfxShading explicit GfxPatchMeshShading(const GfxPatchMeshShading *shading); ~GfxPatchMeshShading() override; - static GfxPatchMeshShading *parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); + static std::unique_ptr parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state); - GfxShading *copy() const override; + std::unique_ptr copy() const override; int getNPatches() const { return nPatches; } const GfxPatch *getPatch(int i) const { return &patches[i]; } @@ -1204,7 +1226,7 @@ class POPPLER_PRIVATE_EXPORT GfxImageColorMap { public: // Constructor. - GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA); + GfxImageColorMap(int bitsA, Object *decode, std::unique_ptr &&colorSpaceA); // Destructor. ~GfxImageColorMap(); @@ -1219,7 +1241,7 @@ class POPPLER_PRIVATE_EXPORT GfxImageColorMap bool isOk() const { return ok; } // Get the color space. - GfxColorSpace *getColorSpace() { return colorSpace; } + GfxColorSpace *getColorSpace() { return colorSpace.get(); } // Get stream decoding info. int getNumPixelComps() const { return nComps; } @@ -1257,7 +1279,7 @@ class POPPLER_PRIVATE_EXPORT GfxImageColorMap private: explicit GfxImageColorMap(const GfxImageColorMap *colorMap); - GfxColorSpace *colorSpace; // the image color space + std::unique_ptr colorSpace; // the image color space int bits; // bits per component int nComps; // number of components in a pixel GfxColorSpace *colorSpace2; // secondary color space @@ -1455,6 +1477,19 @@ class POPPLER_PRIVATE_EXPORT GfxState GfxSubpath *curSubPath; }; + enum LineJoinStyle + { + LineJoinMitre, + LineJoinRound, + LineJoinBevel + }; + + enum LineCapStyle + { + LineCapButt, + LineCapRound, + LineCapProjecting + }; // Construct a default GfxState, for a device with resolution // x , page box , page rotation , and // coordinate system specified by . @@ -1491,10 +1526,10 @@ class POPPLER_PRIVATE_EXPORT GfxState void getFillDeviceN(GfxColor *deviceN) { fillColorSpace->getDeviceN(&fillColor, deviceN); } void getStrokeCMYK(GfxCMYK *cmyk) { strokeColorSpace->getCMYK(&strokeColor, cmyk); } void getStrokeDeviceN(GfxColor *deviceN) { strokeColorSpace->getDeviceN(&strokeColor, deviceN); } - GfxColorSpace *getFillColorSpace() { return fillColorSpace; } - GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; } - GfxPattern *getFillPattern() { return fillPattern; } - GfxPattern *getStrokePattern() { return strokePattern; } + GfxColorSpace *getFillColorSpace() { return fillColorSpace.get(); } + GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace.get(); } + GfxPattern *getFillPattern() { return fillPattern.get(); } + GfxPattern *getStrokePattern() { return strokePattern.get(); } GfxBlendMode getBlendMode() const { return blendMode; } double getFillOpacity() const { return fillOpacity; } double getStrokeOpacity() const { return strokeOpacity; } @@ -1509,8 +1544,8 @@ class POPPLER_PRIVATE_EXPORT GfxState return lineDash; } int getFlatness() const { return flatness; } - int getLineJoin() const { return lineJoin; } - int getLineCap() const { return lineCap; } + LineJoinStyle getLineJoin() const { return lineJoin; } + LineCapStyle getLineCap() const { return lineCap; } double getMiterLimit() const { return miterLimit; } bool getStrokeAdjust() const { return strokeAdjust; } bool getAlphaIsShape() const { return alphaIsShape; } @@ -1574,12 +1609,12 @@ class POPPLER_PRIVATE_EXPORT GfxState void setCTM(double a, double b, double c, double d, double e, double f); void concatCTM(double a, double b, double c, double d, double e, double f); void shiftCTMAndClip(double tx, double ty); - void setFillColorSpace(GfxColorSpace *colorSpace); - void setStrokeColorSpace(GfxColorSpace *colorSpace); + void setFillColorSpace(std::unique_ptr &&colorSpace); + void setStrokeColorSpace(std::unique_ptr &&colorSpace); void setFillColor(const GfxColor *color) { fillColor = *color; } void setStrokeColor(const GfxColor *color) { strokeColor = *color; } - void setFillPattern(GfxPattern *pattern); - void setStrokePattern(GfxPattern *pattern); + void setFillPattern(std::unique_ptr &&pattern); + void setStrokePattern(std::unique_ptr &&pattern); void setBlendMode(GfxBlendMode mode) { blendMode = mode; } void setFillOpacity(double opac) { fillOpacity = opac; } void setStrokeOpacity(double opac) { strokeOpacity = opac; } @@ -1590,8 +1625,8 @@ class POPPLER_PRIVATE_EXPORT GfxState void setLineWidth(double width) { lineWidth = width; } void setLineDash(std::vector &&dash, double start); void setFlatness(int flatness1) { flatness = flatness1; } - void setLineJoin(int lineJoin1) { lineJoin = lineJoin1; } - void setLineCap(int lineCap1) { lineCap = lineCap1; } + void setLineJoin(int lineJoin1) { lineJoin = static_cast(lineJoin1); } + void setLineCap(int lineCap1) { lineCap = static_cast(lineCap1); } void setMiterLimit(double limit) { miterLimit = limit; } void setStrokeAdjust(bool sa) { strokeAdjust = sa; } void setAlphaIsShape(bool ais) { alphaIsShape = ais; } @@ -1616,67 +1651,46 @@ class POPPLER_PRIVATE_EXPORT GfxState #ifdef USE_CMS void setDisplayProfile(const GfxLCMSProfilePtr &localDisplayProfileA); - GfxLCMSProfilePtr getDisplayProfile() - { - return localDisplayProfile; - } + GfxLCMSProfilePtr getDisplayProfile() { return localDisplayProfile; } std::shared_ptr getXYZ2DisplayTransform(); int getCmsRenderingIntent(); static GfxLCMSProfilePtr sRGBProfile; #endif - void setDefaultGrayColorSpace(GfxColorSpace *cs) - { - defaultGrayColorSpace = cs; - } + void setDefaultGrayColorSpace(std::unique_ptr &&cs) { defaultGrayColorSpace = std::move(cs); } - void setDefaultRGBColorSpace(GfxColorSpace *cs) - { - defaultRGBColorSpace = cs; - } + void setDefaultRGBColorSpace(std::unique_ptr &&cs) { defaultRGBColorSpace = std::move(cs); } - void setDefaultCMYKColorSpace(GfxColorSpace *cs) - { - defaultCMYKColorSpace = cs; - } + void setDefaultCMYKColorSpace(std::unique_ptr &&cs) { defaultCMYKColorSpace = std::move(cs); } - GfxColorSpace *copyDefaultGrayColorSpace() + std::unique_ptr copyDefaultGrayColorSpace() { if (defaultGrayColorSpace) { return defaultGrayColorSpace->copy(); } - return new GfxDeviceGrayColorSpace(); + return std::make_unique(); } - GfxColorSpace *copyDefaultRGBColorSpace() + std::unique_ptr copyDefaultRGBColorSpace() { if (defaultRGBColorSpace) { return defaultRGBColorSpace->copy(); } - return new GfxDeviceRGBColorSpace(); + return std::make_unique(); } - GfxColorSpace *copyDefaultCMYKColorSpace() + std::unique_ptr copyDefaultCMYKColorSpace() { if (defaultCMYKColorSpace) { return defaultCMYKColorSpace->copy(); } - return new GfxDeviceCMYKColorSpace(); + return std::make_unique(); } // Add to path. - void moveTo(double x, double y) - { - path->moveTo(curX = x, curY = y); - } - void lineTo(double x, double y) - { - path->lineTo(curX = x, curY = y); - } - void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) - { - path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); - } + void moveTo(double x, double y) { path->moveTo(curX = x, curY = y); } + void lineTo(double x, double y) { path->lineTo(curX = x, curY = y); } + void curveTo(double x1, double y1, double x2, double y2, double x3, double y3) { path->curveTo(x1, y1, x2, y2, curX = x3, curY = y3); } void closePath() { path->close(); @@ -1708,22 +1722,13 @@ class POPPLER_PRIVATE_EXPORT GfxState // Push/pop GfxState on/off stack. GfxState *save(); GfxState *restore(); - bool hasSaves() const - { - return saved != nullptr; - } - bool isParentState(GfxState *state) - { - return saved == state || (saved && saved->isParentState(state)); - } + bool hasSaves() const { return saved != nullptr; } + bool isParentState(GfxState *state) { return saved == state || (saved && saved->isParentState(state)); } // Misc bool parseBlendMode(Object *obj, GfxBlendMode *mode); - ReusablePathIterator *getReusablePath() - { - return new ReusablePathIterator(path); - } + std::unique_ptr getReusablePath() { return std::make_unique(path); } private: double hDPI, vDPI; // resolution @@ -1732,12 +1737,12 @@ class POPPLER_PRIVATE_EXPORT GfxState double pageWidth, pageHeight; // page size (pixels) int rotate; // page rotation angle - GfxColorSpace *fillColorSpace; // fill color space - GfxColorSpace *strokeColorSpace; // stroke color space + std::unique_ptr fillColorSpace; // fill color space + std::unique_ptr strokeColorSpace; // stroke color space GfxColor fillColor; // fill color GfxColor strokeColor; // stroke color - GfxPattern *fillPattern; // fill pattern - GfxPattern *strokePattern; // stroke pattern + std::unique_ptr fillPattern; // fill pattern + std::unique_ptr strokePattern; // stroke pattern GfxBlendMode blendMode; // transparency blend mode double fillOpacity; // fill opacity double strokeOpacity; // stroke opacity @@ -1753,8 +1758,8 @@ class POPPLER_PRIVATE_EXPORT GfxState std::vector lineDash; // line dash double lineDashStart; int flatness; // curve flatness - int lineJoin; // line join style - int lineCap; // line cap style + LineJoinStyle lineJoin; // line join style + LineCapStyle lineCap; // line cap style double miterLimit; // line miter limit bool strokeAdjust; // stroke adjustment bool alphaIsShape; // alpha is shape @@ -1791,9 +1796,9 @@ class POPPLER_PRIVATE_EXPORT GfxState static GfxLCMSProfilePtr XYZProfile; #endif - GfxColorSpace *defaultGrayColorSpace; - GfxColorSpace *defaultRGBColorSpace; - GfxColorSpace *defaultCMYKColorSpace; + std::unique_ptr defaultGrayColorSpace; + std::unique_ptr defaultRGBColorSpace; + std::unique_ptr defaultCMYKColorSpace; }; #endif diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc index a1fd897..7f67c39 100644 --- a/poppler/GlobalParams.cc +++ b/poppler/GlobalParams.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Martin Kretzschmar // Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2024 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2006, 2007 Jeff Muizelaar // Copyright (C) 2006 Takashi Iwai @@ -40,12 +40,15 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018, 2020 Adam Reichold // Copyright (C) 2019 Christian Persch -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2024 Oliver Sander // Copyright (C) 2020 Kai Pastor // Copyright (C) 2021, 2022 Stefan Löffler // Copyright (C) 2021 sunderme // Copyright (C) 2022 Even Rouault // Copyright (C) 2022 Claes Nästén +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2023 Shivodit Gill +// Copyright (C) 2024 Keyu Tao // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -58,15 +61,19 @@ #include #include #include +#include #ifdef _WIN32 # include # include #endif +#ifdef ANDROID +# include +# include +# include +#endif #include "goo/glibc.h" -#include "goo/gmem.h" #include "goo/GooString.h" #include "goo/gfile.h" -#include "goo/gdir.h" #include "Error.h" #include "NameToCharCode.h" #include "CharCodeToUnicode.h" @@ -189,17 +196,17 @@ static const char *get_poppler_fontsdir(void) class SysFontInfo { public: - GooString *name; + const std::unique_ptr name; bool bold; bool italic; bool oblique; bool fixedWidth; - GooString *path; + const std::unique_ptr path; SysFontType type; int fontNum; // for TrueType collections - GooString *substituteName; + const std::unique_ptr substituteName; - SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA); + SysFontInfo(std::unique_ptr &&nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, std::unique_ptr &&pathA, SysFontType typeA, int fontNumA, std::unique_ptr &&substituteNameA); ~SysFontInfo(); SysFontInfo(const SysFontInfo &) = delete; SysFontInfo &operator=(const SysFontInfo &) = delete; @@ -208,25 +215,18 @@ class SysFontInfo bool match(const GooString *nameA, bool boldA, bool italicA) const; }; -SysFontInfo::SysFontInfo(GooString *nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, GooString *pathA, SysFontType typeA, int fontNumA, GooString *substituteNameA) +SysFontInfo::SysFontInfo(std::unique_ptr &&nameA, bool boldA, bool italicA, bool obliqueA, bool fixedWidthA, std::unique_ptr &&pathA, SysFontType typeA, int fontNumA, std::unique_ptr &&substituteNameA) + : name(std::move(nameA)), path(std::move(pathA)), substituteName(std::move(substituteNameA)) { - name = nameA; bold = boldA; italic = italicA; oblique = obliqueA; fixedWidth = fixedWidthA; - path = pathA; type = typeA; fontNum = fontNumA; - substituteName = substituteNameA; } -SysFontInfo::~SysFontInfo() -{ - delete name; - delete path; - delete substituteName; -} +SysFontInfo::~SysFontInfo() = default; bool SysFontInfo::match(const SysFontInfo *fi) const { @@ -262,10 +262,7 @@ class SysFontList void scanWindowsFonts(const std::string &winFontDir); #endif #ifdef WITH_FONTCONFIGURATION_FONTCONFIG - void addFcFont(SysFontInfo *si) - { - fonts.push_back(si); - } + void addFcFont(SysFontInfo *si) { fonts.push_back(si); } #endif private: #ifdef _WIN32 @@ -275,7 +272,7 @@ class SysFontList std::vector fonts; }; -SysFontList::SysFontList() { } +SysFontList::SysFontList() = default; SysFontList::~SysFontList() { @@ -368,7 +365,7 @@ const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, b for (const SysFontInfo *f : fonts) { fi = f; if (fi->match(name2, bold, italic, oblique, fixedWidth)) { - if (std::find(filesToIgnore.begin(), filesToIgnore.end(), fi->path->toStr()) == filesToIgnore.end()) { + if (std::ranges::find(filesToIgnore, fi->path->toStr()) == filesToIgnore.end()) { break; } } @@ -379,7 +376,7 @@ const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, b for (const SysFontInfo *f : fonts) { fi = f; if (fi->match(name2, false, italic)) { - if (std::find(filesToIgnore.begin(), filesToIgnore.end(), fi->path->toStr()) == filesToIgnore.end()) { + if (std::ranges::find(filesToIgnore, fi->path->toStr()) == filesToIgnore.end()) { break; } } @@ -391,7 +388,7 @@ const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, b for (const SysFontInfo *f : fonts) { fi = f; if (fi->match(name2, false, false)) { - if (std::find(filesToIgnore.begin(), filesToIgnore.end(), fi->path->toStr()) == filesToIgnore.end()) { + if (std::ranges::find(filesToIgnore, fi->path->toStr()) == filesToIgnore.end()) { break; } } @@ -411,7 +408,7 @@ const SysFontInfo *SysFontList::find(const std::string &name, bool fixedWidth, b // parsing //------------------------------------------------------------------------ -GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(customPopplerDataDir) +GlobalParams::GlobalParams(const std::string &customPopplerDataDir) : popplerDataDir(customPopplerDataDir) { // scan the encoding in reverse because we want the lowest-numbered // index for each char name ('space' is encoded twice) @@ -430,8 +427,8 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(cu profileCommands = false; errQuiet = false; - cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize); - unicodeToUnicodeCache = new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize); + cidToUnicodeCache = std::make_unique(cidToUnicodeCacheSize); + unicodeToUnicodeCache = std::make_unique(unicodeToUnicodeCacheSize); unicodeMapCache = new UnicodeMapCache(); cMapCache = new CMapCache(); @@ -450,13 +447,13 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(cu // set up the residentUnicodeMaps table residentUnicodeMaps.reserve(6); - UnicodeMap map = { "Latin1", false, latin1UnicodeMapRanges, latin1UnicodeMapLen }; + UnicodeMap map = { "Latin1", false, std::span(latin1UnicodeMapRanges, latin1UnicodeMapLen) }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); - map = { "ASCII7", false, ascii7UnicodeMapRanges, ascii7UnicodeMapLen }; + map = { "ASCII7", false, std::span(ascii7UnicodeMapRanges, ascii7UnicodeMapLen) }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); - map = { "Symbol", false, symbolUnicodeMapRanges, symbolUnicodeMapLen }; + map = { "Symbol", false, std::span(symbolUnicodeMapRanges, symbolUnicodeMapLen) }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); - map = { "ZapfDingbats", false, zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen }; + map = { "ZapfDingbats", false, std::span(zapfDingbatsUnicodeMapRanges, zapfDingbatsUnicodeMapLen) }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); map = { "UTF-8", true, &mapUTF8 }; residentUnicodeMaps.emplace(map.getEncodingName(), std::move(map)); @@ -468,49 +465,30 @@ GlobalParams::GlobalParams(const char *customPopplerDataDir) : popplerDataDir(cu void GlobalParams::scanEncodingDirs() { - GDir *dir; - std::unique_ptr entry; - const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR; - - // allocate buffer large enough to append "/nameToUnicode" - size_t bufSize = strlen(dataRoot) + strlen("/nameToUnicode") + 1; - char *dataPathBuffer = new char[bufSize]; + std::string dataRoot = !popplerDataDir.empty() ? popplerDataDir : std::string { POPPLER_DATADIR }; - snprintf(dataPathBuffer, bufSize, "%s/nameToUnicode", dataRoot); - dir = new GDir(dataPathBuffer, true); - while (entry = dir->getNextEntry(), entry != nullptr) { - if (!entry->isDir()) { - parseNameToUnicode(entry->getFullPath()); + std::error_code ec; // if ec is set, we also get the end iterator, so that's kind of okay. If not creating with a error code, we get an exception if poppler data is missing + for (const auto &entry : std::filesystem::directory_iterator { dataRoot + "/nameToUnicode", ec }) { + if (entry.is_regular_file()) { + parseNameToUnicode(entry.path()); } } - delete dir; - snprintf(dataPathBuffer, bufSize, "%s/cidToUnicode", dataRoot); - dir = new GDir(dataPathBuffer, false); - while (entry = dir->getNextEntry(), entry != nullptr) { - addCIDToUnicode(entry->getName(), entry->getFullPath()); + for (const auto &entry : std::filesystem::directory_iterator { dataRoot + "/cidToUnicode", ec }) { + addCIDToUnicode(entry.path().filename().string(), entry.path().string()); } - delete dir; - snprintf(dataPathBuffer, bufSize, "%s/unicodeMap", dataRoot); - dir = new GDir(dataPathBuffer, false); - while (entry = dir->getNextEntry(), entry != nullptr) { - addUnicodeMap(entry->getName(), entry->getFullPath()); + for (const auto &entry : std::filesystem::directory_iterator { dataRoot + "/unicodeMap", ec }) { + addUnicodeMap(entry.path().filename().string(), entry.path().string()); } - delete dir; - snprintf(dataPathBuffer, bufSize, "%s/cMap", dataRoot); - dir = new GDir(dataPathBuffer, false); - while (entry = dir->getNextEntry(), entry != nullptr) { - addCMapDir(entry->getName(), entry->getFullPath()); - toUnicodeDirs.push_back(entry->getFullPath()->copy()); + for (const auto &entry : std::filesystem::directory_iterator { dataRoot + "/cMap", ec }) { + addCMapDir(entry.path().filename().string(), entry.path().string()); + toUnicodeDirs.push_back(entry.path().string()); } - delete dir; - - delete[] dataPathBuffer; } -void GlobalParams::parseNameToUnicode(const GooString *name) +void GlobalParams::parseNameToUnicode(const std::filesystem::path &name) { char *tok1, *tok2; FILE *f; @@ -519,8 +497,8 @@ void GlobalParams::parseNameToUnicode(const GooString *name) Unicode u; char *tokptr; - if (!(f = openFile(name->c_str(), "r"))) { - error(errIO, -1, "Couldn't open 'nameToUnicode' file '{0:t}'", name); + if (!(f = openFile(name.string().c_str(), "r"))) { + error(errIO, -1, "Couldn't open 'nameToUnicode' file '{0:s}'", name.string().c_str()); return; } line = 1; @@ -531,26 +509,26 @@ void GlobalParams::parseNameToUnicode(const GooString *name) sscanf(tok1, "%x", &u); nameToUnicodeText->add(tok2, u); } else { - error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:t}:{1:d})", name, line); + error(errConfig, -1, "Bad line in 'nameToUnicode' file ({0:s}:{1:d})", name.string().c_str(), line); } ++line; } fclose(f); } -void GlobalParams::addCIDToUnicode(const GooString *collection, const GooString *fileName) +void GlobalParams::addCIDToUnicode(std::string &&collection, std::string &&fileName) { - cidToUnicodes[collection->toStr()] = fileName->toStr(); + cidToUnicodes[collection] = fileName; } -void GlobalParams::addUnicodeMap(const GooString *encodingName, const GooString *fileName) +void GlobalParams::addUnicodeMap(std::string &&encodingName, std::string &&fileName) { - unicodeMaps[encodingName->toStr()] = fileName->toStr(); + unicodeMaps[encodingName] = fileName; } -void GlobalParams::addCMapDir(const GooString *collection, const GooString *dir) +void GlobalParams::addCMapDir(std::string &&collection, std::string &&dir) { - cMapDirs.emplace(collection->toStr(), dir->toStr()); + cMapDirs.emplace(collection, dir); } bool GlobalParams::parseYesNo2(const char *token, bool *flag) @@ -571,14 +549,9 @@ GlobalParams::~GlobalParams() delete nameToUnicodeZapfDingbats; delete nameToUnicodeText; - for (auto entry : toUnicodeDirs) { - delete entry; - } delete sysFonts; delete textEncoding; - delete cidToUnicodeCache; - delete unicodeToUnicodeCache; delete unicodeMapCache; delete cMapCache; } @@ -635,15 +608,15 @@ FILE *GlobalParams::getUnicodeMapFile(const std::string &encodingName) return file; } -FILE *GlobalParams::findCMapFile(const GooString *collection, const GooString *cMapName) +FILE *GlobalParams::findCMapFile(const GooString &collection, const GooString &cMapName) { FILE *file = nullptr; globalParamsLocker(); - const auto collectionCMapDirs = cMapDirs.equal_range(collection->toStr()); + const auto collectionCMapDirs = cMapDirs.equal_range(collection.toStr()); for (auto cMapDir = collectionCMapDirs.first; cMapDir != collectionCMapDirs.second; ++cMapDir) { auto *const path = new GooString(cMapDir->second); - appendToPath(path, cMapName->c_str()); + appendToPath(path, cMapName.c_str()); file = openFile(path->c_str(), "r"); delete path; if (file) { @@ -660,8 +633,8 @@ FILE *GlobalParams::findToUnicodeFile(const GooString *name) FILE *f; globalParamsLocker(); - for (const GooString *dir : toUnicodeDirs) { - fileName = appendToPath(dir->copy(), name->c_str()); + for (const std::string &dir : toUnicodeDirs) { + fileName = appendToPath(new GooString(dir), name->c_str()); f = openFile(fileName->c_str(), "r"); delete fileName; if (f) { @@ -712,7 +685,7 @@ static const char *getFontLang(const GfxFont *font) } else if (strcmp(collection->c_str(), "Adobe-Identity") == 0) { lang = "xx"; } else { - error(errUnimplemented, -1, "Unknown CID font collection, please report to poppler bugzilla."); + error(errUnimplemented, -1, "Unknown CID font collection: {0:t}. If this is expected to be a valid PDF document, please report to poppler bugtracker.", collection); lang = "xx"; } } else { @@ -744,7 +717,7 @@ static FcPattern *buildFcPattern(const GfxFont *font, const GooString *base14Nam // remove the - from the names, for some reason, Fontconfig does not // understand "MS-Mincho" but does with "MS Mincho" - std::replace(fontName.begin(), fontName.end(), '-', ' '); + std::ranges::replace(fontName, '-', ' '); size_t start = std::string::npos; findModifier(fontName, modStart, "Regular", start); @@ -880,25 +853,23 @@ static FcPattern *buildFcPattern(const GfxFont *font, const GooString *base14Nam } #endif -GooString *GlobalParams::findFontFile(const std::string &fontName) +std::optional GlobalParams::findFontFile(const std::string &fontName) { - GooString *path = nullptr; - setupBaseFonts(POPPLER_FONTSDIR); globalParamsLocker(); const auto fontFile = fontFiles.find(fontName); if (fontFile != fontFiles.end()) { - path = new GooString(fontFile->second); + return fontFile->second; } - return path; + return {}; } #if defined(WITH_FONTCONFIGURATION_FONTCONFIG) || defined(WITH_FONTCONFIGURATION_WIN32) static bool supportedFontForEmbedding(Unicode uChar, const char *filepath, int faceIndex) { - if (!GooString::endsWith(filepath, ".ttf") && !GooString::endsWith(filepath, ".ttc") && !GooString::endsWith(filepath, ".otf")) { + if (!std::string_view(filepath).ends_with(".ttf") && !std::string_view(filepath).ends_with(".ttc") && !std::string_view(filepath).ends_with(".otf")) { // for now we only support ttf, ttc, otf fonts return false; } @@ -938,29 +909,29 @@ static bool supportedFontForEmbedding(Unicode uChar, const char *filepath, int f // not needed for fontconfig void GlobalParams::setupBaseFonts(const char *) { } -GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font) +std::optional GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName) { SysFontType type; int fontNum; - return findSystemFontFile(font, &type, &fontNum, nullptr, base14Name); + return findSystemFontFile(font, &type, &fontNum, substituteFontName, base14Name); } -GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) +std::optional GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) { const SysFontInfo *fi = nullptr; FcPattern *p = nullptr; - GooString *path = nullptr; + std::optional path; const std::optional &fontName = font->getName(); GooString substituteName; if (!fontName) { - return nullptr; + return {}; } globalParamsLocker(); if ((fi = sysFonts->find(*fontName, font->isFixedWidth(), true))) { - path = fi->path->copy(); + path = fi->path->toStr(); *type = fi->type; *fontNum = fi->fontNum; substituteName.Set(fi->substituteName->c_str()); @@ -1052,10 +1023,10 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty *fontNum = 0; *type = (!strncasecmp(ext, ".ttc", 4)) ? sysFontTTC : sysFontTTF; FcPatternGetInteger(set->fonts[i], FC_INDEX, 0, fontNum); - SysFontInfo *sfi = new SysFontInfo(new GooString(*fontName), bold, italic, oblique, font->isFixedWidth(), new GooString((char *)s), *type, *fontNum, substituteName.copy()); + SysFontInfo *sfi = new SysFontInfo(std::make_unique(*fontName), bold, italic, oblique, font->isFixedWidth(), std::make_unique((char *)s), *type, *fontNum, substituteName.copy()); sysFonts->addFcFont(sfi); fi = sfi; - path = new GooString((char *)s); + path = std::string((char *)s); } else if (!strncasecmp(ext, ".pfa", 4) || !strncasecmp(ext, ".pfb", 4)) { int weight, slant; bool bold = font->isBold(); @@ -1075,10 +1046,10 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty *fontNum = 0; *type = (!strncasecmp(ext, ".pfa", 4)) ? sysFontPFA : sysFontPFB; FcPatternGetInteger(set->fonts[i], FC_INDEX, 0, fontNum); - SysFontInfo *sfi = new SysFontInfo(new GooString(*fontName), bold, italic, oblique, font->isFixedWidth(), new GooString((char *)s), *type, *fontNum, substituteName.copy()); + SysFontInfo *sfi = new SysFontInfo(std::make_unique(*fontName), bold, italic, oblique, font->isFixedWidth(), std::make_unique((char *)s), *type, *fontNum, substituteName.copy()); sysFonts->addFcFont(sfi); fi = sfi; - path = new GooString((char *)s); + path = std::string((char *)s); } else { continue; } @@ -1094,8 +1065,8 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty } FcFontSetDestroy(set); } - if (path == nullptr && (fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { - path = fi->path->copy(); + if (!path && (fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { + path = fi->path->toStr(); *type = fi->type; *fontNum = fi->fontNum; } @@ -1116,6 +1087,7 @@ FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(co FcConfigSubstitute(nullptr, p, FcMatchPattern); FcDefaultSubstitute(p); if (p) { + const std::unique_ptr pDeleter(p, [](FcPattern *pattern) { FcPatternDestroy(pattern); }); FcResult res; FcFontSet *fontSet = FcFontSort(nullptr, p, FcFalse, nullptr, &res); if (fontSet) { @@ -1128,7 +1100,7 @@ FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(co FcPatternGetInteger(fontSet->fonts[i], FC_INDEX, 0, &faceIndex); const std::string sFilePath = reinterpret_cast(fcFilePath); - if (std::find(filesToIgnore.begin(), filesToIgnore.end(), sFilePath) == filesToIgnore.end()) { + if (std::ranges::find(filesToIgnore, sFilePath) == filesToIgnore.end()) { return FamilyStyleFontSearchResult(sFilePath, faceIndex); } } @@ -1158,11 +1130,16 @@ UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, co int faceIndex = 0; FcChar8 *fcFamily = nullptr; FcChar8 *fcStyle = nullptr; + FcCharSet *fcCharSet = nullptr; FcPatternGetString(fontSet->fonts[i], FC_FILE, 0, &fcFilePath); FcPatternGetInteger(fontSet->fonts[i], FC_INDEX, 0, &faceIndex); FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &fcFamily); FcPatternGetString(fontSet->fonts[i], FC_STYLE, 0, &fcStyle); - if (!fcFilePath || !fcFamily || !fcStyle) { + FcPatternGetCharSet(fontSet->fonts[i], FC_CHARSET, 0, &fcCharSet); + if (!fcFilePath || !fcFamily || !fcStyle || !fcCharSet) { + continue; + } + if (!FcCharSetHasChar(fcCharSet, uChar)) { continue; } @@ -1176,11 +1153,156 @@ UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, co return {}; } +#elif defined(WITH_FONTCONFIGURATION_ANDROID) +// Uses the font file mapping created by GlobalParams::setupBaseFonts +// to return the path to a base-14 font file +std::optional GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName) +{ + return findFontFile(base14Name->toStr()); +} + +# if __ANDROID_API__ >= 29 + +// This struct is used by the AFontMatcher unique_ptr for destroying the +// AFontMatcher object +struct AFontMatcherDestroyer +{ + void operator()(AFontMatcher *fontmatcher) { AFontMatcher_destroy(fontmatcher); } +}; + +// This struct is used by the AFontMatcher unique_ptr for destroying the +// AFont object +struct AFontDestroyer +{ + void operator()(AFont *afont) { AFont_close(afont); } +}; + +# endif + +std::optional GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) +{ + const std::optional &fontName = font->getName(); + + if (!fontName) { + return {}; + } + + globalParamsLocker(); + +# if __ANDROID_API__ >= 29 + // If font is not found in the default base-14 fonts, + // use Android-NDK's AFontMatcher API instead. + // Documentation for AFontMatcher API can be found at: + // https://developer.android.com/ndk/reference/group/font + std::string genericFontFamily = "serif"; + + if (!font->isSerif()) { + genericFontFamily = "sans-serif"; + } else if (font->isFixedWidth()) { + genericFontFamily = "monospace"; + } + + std::unique_ptr fontmatcher { AFontMatcher_create() }; + + // Set font weight and italics for the font. + AFontMatcher_setStyle(fontmatcher.get(), font->getWeight() * 100, font->isItalic()); + + // Get font match and the font file's path + std::unique_ptr afont { AFontMatcher_match(fontmatcher.get(), genericFontFamily.c_str(), (uint16_t *)u"A", 1, nullptr) }; + std::string path = std::string(AFont_getFontFilePath(afont.get())); + + // Set the type of font. Fonts returned by AFontMatcher are of + // four possible types - ttf, otf, ttc, otc. + if (path.ends_with(".ttf") || path.ends_with(".otf")) { + *type = sysFontTTF; + } else if (path.ends_with(".ttc") || path.ends_with(".otc")) { + *type = sysFontTTC; + } +# else +# pragma message("Compiling without AFontMatcher API due to Android API version being lower than 29.") +# endif + + return path; +} + +static struct +{ + const char *name; + const char *otFileName; +} displayFontTab[] = { { "Courier", "NimbusMonoPS-Regular.otf" }, + { "Courier-Bold", "NimbusMonoPS-Bold.otf" }, + { "Courier-BoldOblique", "NimbusMonoPS-BoldItalic.otf" }, + { "Courier-Oblique", "NimbusMonoPS-Italic.otf" }, + { "Helvetica", "NimbusSans-Regular.otf" }, + { "Helvetica-Bold", "NimbusSans-Bold.otf" }, + { "Helvetica-BoldOblique", "NimbusSans-BoldItalic.otf" }, + { "Helvetica-Oblique", "NimbusSans-Italic.otf" }, + { "Symbol", "StandardSymbolsPS.otf" }, + { "Times-Bold", "NimbusRoman-Bold.otf" }, + { "Times-BoldItalic", "NimbusRoman-BoldItalic.otf" }, + { "Times-Italic", "NimbusRoman-Italic.otf" }, + { "Times-Roman", "NimbusRoman-Regular.otf" }, + { "ZapfDingbats", "D050000L.otf" }, + { nullptr, nullptr } }; + +// The path to the font directory. Set by GlobalParams::setFontDir() +static std::string displayFontDir; + +// This method creates a mapping from base-14 font names to their +// paths on the file system. On Android, it searches within the +// directory set by GlobalParams::setFontDir(). +void GlobalParams::setupBaseFonts(const char *dir) +{ + FILE *f; + int i; + + for (i = 0; displayFontTab[i].name; ++i) { + if (fontFiles.count(displayFontTab[i].name) > 0) { + continue; + } + + std::unique_ptr fontName = std::make_unique(displayFontTab[i].name); + std::unique_ptr fileName; + if (dir) { + fileName.reset(appendToPath(new GooString(dir), displayFontTab[i].otFileName)); + if ((f = openFile(fileName->c_str(), "rb"))) { + fclose(f); + } else { + fileName.reset(); + } + } + if (!displayFontDir.empty()) { + fileName.reset(appendToPath(new GooString(displayFontDir), displayFontTab[i].otFileName)); + if ((f = openFile(fileName->c_str(), "rb"))) { + fclose(f); + } else { + fileName.reset(); + } + } + if (!fileName) { + error(errConfig, -1, "No display font for '{0:s}'", displayFontTab[i].name); + continue; + } + addFontFile(fontName->toStr(), fileName->toStr()); + } +} + +FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector &filesToIgnore) +{ + error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForFamilyAndStyle not implemented for this platform"); + return {}; +} + +UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate) +{ + error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForUChar not implemented for this platform"); + return {}; +} #elif defined(WITH_FONTCONFIGURATION_WIN32) # include "GlobalParamsWin.cc" -GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font) +std::optional GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString * /*substituteFontName*/) { return findFontFile(base14Name->toStr()); } @@ -1199,7 +1321,7 @@ UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, co return {}; } -GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font) +std::optional GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString * /*substituteFontName*/) { return findFontFile(base14Name->toStr()); } @@ -1258,23 +1380,23 @@ void GlobalParams::setupBaseFonts(const char *dir) error(errConfig, -1, "No display font for '{0:s}'", displayFontTab[i].name); continue; } - addFontFile(fontName.get(), fileName.get()); + addFontFile(fontName->toStr(), fileName->toStr()); } } -GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString * /*substituteFontName*/, const GooString * /*base14Name*/) +std::optional GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString * /*substituteFontName*/, const GooString * /*base14Name*/) { const SysFontInfo *fi; - GooString *path; + std::optional path; const std::optional &fontName = font->getName(); - if (!fontName) - return nullptr; + if (!fontName) { + return {}; + } - path = nullptr; globalParamsLocker(); if ((fi = sysFonts->find(*fontName, font->isFixedWidth(), false))) { - path = fi->path->copy(); + path = fi->path->toStr(); *type = fi->type; *fontNum = fi->fontNum; } @@ -1317,9 +1439,9 @@ bool GlobalParams::getErrQuiet() return errQuiet; } -CharCodeToUnicode *GlobalParams::getCIDToUnicode(const GooString *collection) +std::shared_ptr GlobalParams::getCIDToUnicode(const GooString *collection) { - CharCodeToUnicode *ctu; + std::shared_ptr ctu; globalParamsLocker(); if (!(ctu = cidToUnicodeCache->getCharCodeToUnicode(collection))) { @@ -1346,7 +1468,7 @@ const UnicodeMap *GlobalParams::getUnicodeMap(const std::string &encodingName) return map; } -std::shared_ptr GlobalParams::getCMap(const GooString *collection, const GooString *cMapName) +std::shared_ptr GlobalParams::getCMap(const GooString &collection, const GooString &cMapName) { cMapCacheLocker(); return cMapCache->getCMap(collection, cMapName); @@ -1360,6 +1482,7 @@ const UnicodeMap *GlobalParams::getTextEncoding() std::vector GlobalParams::getEncodingNames() { std::vector result; + result.reserve(residentUnicodeMaps.size() + unicodeMaps.size()); for (const auto &unicodeMap : residentUnicodeMaps) { result.push_back(unicodeMap.first); } @@ -1373,10 +1496,10 @@ std::vector GlobalParams::getEncodingNames() // functions to set parameters //------------------------------------------------------------------------ -void GlobalParams::addFontFile(const GooString *fontName, const GooString *path) +void GlobalParams::addFontFile(const std::string &fontName, const std::string &path) { globalParamsLocker(); - fontFiles[fontName->toStr()] = path->toStr(); + fontFiles[fontName] = path; } void GlobalParams::setTextEncoding(const char *encodingName) @@ -1404,12 +1527,21 @@ void GlobalParams::setErrQuiet(bool errQuietA) errQuiet = errQuietA; } +#ifdef ANDROID +void GlobalParams::setFontDir(const std::string &fontDir) +{ +# if defined(WITH_FONTCONFIGURATION_ANDROID) + displayFontDir = fontDir; +# endif +} +#endif + GlobalParamsIniter::GlobalParamsIniter(ErrorCallback errorCallback) { const std::scoped_lock lock { mutex }; if (count == 0) { - globalParams = std::make_unique(!customDataDir.empty() ? customDataDir.c_str() : nullptr); + globalParams = std::make_unique(customDataDir); setErrorCallback(errorCallback); } diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h index 67399f6..3bd9d7b 100644 --- a/poppler/GlobalParams.h +++ b/poppler/GlobalParams.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2007-2010, 2012, 2015, 2017-2024 Albert Astals Cid // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2006 Kristian Høgsberg @@ -28,6 +28,8 @@ // Copyright (C) 2013 Jason Crain // Copyright (C) 2018, 2020 Adam Reichold // Copyright (C) 2019 Oliver Sander +// Copyright (C) 2023 Shivodit Gill +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -48,7 +50,9 @@ #include #include #include +#include #include +#include class GooString; class NameToCharCode; @@ -109,7 +113,7 @@ class POPPLER_PRIVATE_EXPORT GlobalParams { public: // Initialize the global parameters - explicit GlobalParams(const char *customPopplerDataDir = nullptr); + explicit GlobalParams(const std::string &customPopplerDataDir = {}); ~GlobalParams(); @@ -132,11 +136,11 @@ class POPPLER_PRIVATE_EXPORT GlobalParams UnicodeMap *getResidentUnicodeMap(const std::string &encodingName); FILE *getUnicodeMapFile(const std::string &encodingName); - FILE *findCMapFile(const GooString *collection, const GooString *cMapName); + FILE *findCMapFile(const GooString &collection, const GooString &cMapName); FILE *findToUnicodeFile(const GooString *name); - GooString *findFontFile(const std::string &fontName); - GooString *findBase14FontFile(const GooString *base14Name, const GfxFont *font); - GooString *findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName = nullptr, const GooString *base14Name = nullptr); + std::optional findFontFile(const std::string &fontName); + std::optional findBase14FontFile(const GooString *base14Name, const GfxFont *font, GooString *substituteFontName = nullptr); + std::optional findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName = nullptr, const GooString *base14Name = nullptr); FamilyStyleFontSearchResult findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle, const std::vector &filesToIgnore = {}); UCharFontSearchResult findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate); std::string getTextEncodingName() const; @@ -144,9 +148,9 @@ class POPPLER_PRIVATE_EXPORT GlobalParams bool getProfileCommands(); bool getErrQuiet(); - CharCodeToUnicode *getCIDToUnicode(const GooString *collection); + std::shared_ptr getCIDToUnicode(const GooString *collection); const UnicodeMap *getUnicodeMap(const std::string &encodingName); - std::shared_ptr getCMap(const GooString *collection, const GooString *cMapName); + std::shared_ptr getCMap(const GooString &collection, const GooString &cMapName); const UnicodeMap *getTextEncoding(); const UnicodeMap *getUtf8Map(); @@ -154,21 +158,23 @@ class POPPLER_PRIVATE_EXPORT GlobalParams std::vector getEncodingNames(); //----- functions to set parameters - void addFontFile(const GooString *fontName, const GooString *path); + void addFontFile(const std::string &fontName, const std::string &path); void setTextEncoding(const char *encodingName); void setPrintCommands(bool printCommandsA); void setProfileCommands(bool profileCommandsA); void setErrQuiet(bool errQuietA); - +#ifdef ANDROID + static void setFontDir(const std::string &fontDir); +#endif static bool parseYesNo2(const char *token, bool *flag); private: - void parseNameToUnicode(const GooString *name); + void parseNameToUnicode(const std::filesystem::path &name); void scanEncodingDirs(); - void addCIDToUnicode(const GooString *collection, const GooString *fileName); - void addUnicodeMap(const GooString *encodingName, const GooString *fileName); - void addCMapDir(const GooString *collection, const GooString *dir); + void addCIDToUnicode(std::string &&collection, std::string &&fileName); + void addUnicodeMap(std::string &&encodingName, std::string &&fileName); + void addCMapDir(std::string &&collection, std::string &&dir); //----- static tables @@ -192,7 +198,7 @@ class POPPLER_PRIVATE_EXPORT GlobalParams std::unordered_map unicodeMaps; // list of CMap dirs, indexed by collection std::unordered_multimap cMapDirs; - std::vector toUnicodeDirs; // list of ToUnicode CMap dirs + std::vector toUnicodeDirs; // list of ToUnicode CMap dirs bool baseFontsInitialized; #ifdef _WIN32 // windows font substitutes (for CID fonts) @@ -207,8 +213,8 @@ class POPPLER_PRIVATE_EXPORT GlobalParams bool profileCommands; // profile the drawing commands bool errQuiet; // suppress error messages? - CharCodeToUnicodeCache *cidToUnicodeCache; - CharCodeToUnicodeCache *unicodeToUnicodeCache; + std::unique_ptr cidToUnicodeCache; + std::unique_ptr unicodeToUnicodeCache; UnicodeMapCache *unicodeMapCache; CMapCache *cMapCache; @@ -218,7 +224,7 @@ class POPPLER_PRIVATE_EXPORT GlobalParams mutable std::recursive_mutex unicodeMapCacheMutex; mutable std::recursive_mutex cMapCacheMutex; - const char *popplerDataDir; + std::string popplerDataDir; }; class POPPLER_PRIVATE_EXPORT GlobalParamsIniter diff --git a/poppler/GlobalParamsWin.cc b/poppler/GlobalParamsWin.cc index 93b47d0..63ca373 100644 --- a/poppler/GlobalParamsWin.cc +++ b/poppler/GlobalParamsWin.cc @@ -4,17 +4,18 @@ // Copyright (C) 2010, 2012 Hib Eris // Copyright (C) 2012, 2013 Thomas Freitag // Copyright (C) 2012 Suzuki Toshiya - // Copyright (C) 2012, 2017 Adrian Johnson + // Copyright (C) 2012, 2017, 2024 Adrian Johnson // Copyright (C) 2012 Mark Brand // Copyright (C) 2013, 2018, 2019 Adam Reichold // Copyright (C) 2013 Dmytro Morgun // Copyright (C) 2017 Christoph Cullmann - // Copyright (C) 2017, 2018, 2020-2022 Albert Astals Cid + // Copyright (C) 2017, 2018, 2020-2024 Albert Astals Cid // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2019 Christian Persch // Copyright (C) 2019 Oliver Sander // Copyright (C) 2021 Stefan Löffler // Copyright (C) 2021 sunderme + // Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela TODO: instead of a fixed mapping defined in displayFontTab, it could scan the whole fonts directory, parse TTF files and build font @@ -61,99 +62,97 @@ description for all fonts available in Windows. That's how MuPDF works. static const struct { const char *name; - const char *t1FileName; - const char *ttFileName; + const std::vector fileNames; bool warnIfMissing; -} displayFontTab[] = { { "Courier", "n022003l.pfb", "cour.ttf", true }, - { "Courier-Bold", "n022004l.pfb", "courbd.ttf", true }, - { "Courier-BoldOblique", "n022024l.pfb", "courbi.ttf", true }, - { "Courier-Oblique", "n022023l.pfb", "couri.ttf", true }, - { "Helvetica", "n019003l.pfb", "arial.ttf", true }, - { "Helvetica-Bold", "n019004l.pfb", "arialbd.ttf", true }, - { "Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf", true }, - { "Helvetica-Oblique", "n019023l.pfb", "ariali.ttf", true }, - // TODO: not sure if "symbol.ttf" is right - { "Symbol", "s050000l.pfb", "symbol.ttf", true }, - { "Times-Bold", "n021004l.pfb", "timesbd.ttf", true }, - { "Times-BoldItalic", "n021024l.pfb", "timesbi.ttf", true }, - { "Times-Italic", "n021023l.pfb", "timesi.ttf", true }, - { "Times-Roman", "n021003l.pfb", "times.ttf", true }, +} displayFontTab[] = { { "Courier", { "n022003l.pfb", "cour.ttf" }, true }, + { "Courier-Bold", { "n022004l.pfb", "courbd.ttf" }, true }, + { "Courier-BoldOblique", { "n022024l.pfb", "courbi.ttf" }, true }, + { "Courier-Oblique", { "n022023l.pfb", "couri.ttf" }, true }, + { "Helvetica", { "n019003l.pfb", "arial.ttf" }, true }, + { "Helvetica-Bold", { "n019004l.pfb", "arialbd.ttf" }, true }, + { "Helvetica-BoldOblique", { "n019024l.pfb", "arialbi.ttf" }, true }, + { "Helvetica-Oblique", { "n019023l.pfb", "ariali.ttf" }, true }, + { "Symbol", { "s050000l.pfb", "StandardSymbolsPS.otf", "StandardSymbolsPS.ttf" }, true }, + { "Times-Bold", { "n021004l.pfb", "timesbd.ttf" }, true }, + { "Times-BoldItalic", { "n021024l.pfb", "timesbi.ttf" }, true }, + { "Times-Italic", { "n021023l.pfb", "timesi.ttf" }, true }, + { "Times-Roman", { "n021003l.pfb", "times.ttf" }, true }, // TODO: not sure if "wingding.ttf" is right - { "ZapfDingbats", "d050000l.pfb", "wingding.ttf", true }, + { "ZapfDingbats", { "d050000l.pfb", "wingding.ttf" }, true }, // those seem to be frequently accessed by PDF files and I kind of guess // which font file do the refer to - { "Palatino", nullptr, "pala.ttf", true }, - { "Palatino-Roman", nullptr, "pala.ttf", true }, - { "Palatino-Bold", nullptr, "palab.ttf", true }, - { "Palatino-Italic", nullptr, "palai.ttf", true }, - { "Palatino,Italic", nullptr, "palai.ttf", true }, - { "Palatino-BoldItalic", nullptr, "palabi.ttf", true }, - - { "ArialBlack", nullptr, "arialbd.ttf", true }, - - { "ArialNarrow", nullptr, "arialn.ttf", true }, - { "ArialNarrow,Bold", nullptr, "arialnb.ttf", true }, - { "ArialNarrow,Italic", nullptr, "arialni.ttf", true }, - { "ArialNarrow,BoldItalic", nullptr, "arialnbi.ttf", true }, - { "ArialNarrow-Bold", nullptr, "arialnb.ttf", true }, - { "ArialNarrow-Italic", nullptr, "arialni.ttf", true }, - { "ArialNarrow-BoldItalic", nullptr, "arialnbi.ttf", true }, - - { "HelveticaNarrow", nullptr, "arialn.ttf", true }, - { "HelveticaNarrow,Bold", nullptr, "arialnb.ttf", true }, - { "HelveticaNarrow,Italic", nullptr, "arialni.ttf", true }, - { "HelveticaNarrow,BoldItalic", nullptr, "arialnbi.ttf", true }, - { "HelveticaNarrow-Bold", nullptr, "arialnb.ttf", true }, - { "HelveticaNarrow-Italic", nullptr, "arialni.ttf", true }, - { "HelveticaNarrow-BoldItalic", nullptr, "arialnbi.ttf", true }, - - { "BookAntiqua", nullptr, "bkant.ttf", true }, - { "BookAntiqua,Bold", nullptr, "bkant.ttf", true }, - { "BookAntiqua,Italic", nullptr, "bkant.ttf", true }, - { "BookAntiqua,BoldItalic", nullptr, "bkant.ttf", true }, - { "BookAntiqua-Bold", nullptr, "bkant.ttf", true }, - { "BookAntiqua-Italic", nullptr, "bkant.ttf", true }, - { "BookAntiqua-BoldItalic", nullptr, "bkant.ttf", true }, - - { "Verdana", nullptr, "verdana.ttf", true }, - { "Verdana,Bold", nullptr, "verdanab.ttf", true }, - { "Verdana,Italic", nullptr, "verdanai.ttf", true }, - { "Verdana,BoldItalic", nullptr, "verdanaz.ttf", true }, - { "Verdana-Bold", nullptr, "verdanab.ttf", true }, - { "Verdana-Italic", nullptr, "verdanai.ttf", true }, - { "Verdana-BoldItalic", nullptr, "verdanaz.ttf", true }, - - { "Tahoma", nullptr, "tahoma.ttf", true }, - { "Tahoma,Bold", nullptr, "tahomabd.ttf", true }, - { "Tahoma,Italic", nullptr, "tahoma.ttf", true }, - { "Tahoma,BoldItalic", nullptr, "tahomabd.ttf", true }, - { "Tahoma-Bold", nullptr, "tahomabd.ttf", true }, - { "Tahoma-Italic", nullptr, "tahoma.ttf", true }, - { "Tahoma-BoldItalic", nullptr, "tahomabd.ttf", true }, - - { "CCRIKH+Verdana", nullptr, "verdana.ttf", true }, - { "CCRIKH+Verdana,Bold", nullptr, "verdanab.ttf", true }, - { "CCRIKH+Verdana,Italic", nullptr, "verdanai.ttf", true }, - { "CCRIKH+Verdana,BoldItalic", nullptr, "verdanaz.ttf", true }, - { "CCRIKH+Verdana-Bold", nullptr, "verdanab.ttf", true }, - { "CCRIKH+Verdana-Italic", nullptr, "verdanai.ttf", true }, - { "CCRIKH+Verdana-BoldItalic", nullptr, "verdanaz.ttf", true }, - - { "Georgia", nullptr, "georgia.ttf", true }, - { "Georgia,Bold", nullptr, "georgiab.ttf", true }, - { "Georgia,Italic", nullptr, "georgiai.ttf", true }, - { "Georgia,BoldItalic", nullptr, "georgiaz.ttf", true }, - { "Georgia-Bold", nullptr, "georgiab.ttf", true }, - { "Georgia-Italic", nullptr, "georgiai.ttf", true }, - { "Georgia-BoldItalic", nullptr, "georgiaz.ttf", true }, + { "Palatino", { "pala.ttf" }, true }, + { "Palatino-Roman", { "pala.ttf" }, true }, + { "Palatino-Bold", { "palab.ttf" }, true }, + { "Palatino-Italic", { "palai.ttf" }, true }, + { "Palatino,Italic", { "palai.ttf" }, true }, + { "Palatino-BoldItalic", { "palabi.ttf" }, true }, + + { "ArialBlack", { "arialbd.ttf" }, true }, + + { "ArialNarrow", { "arialn.ttf" }, true }, + { "ArialNarrow,Bold", { "arialnb.ttf" }, true }, + { "ArialNarrow,Italic", { "arialni.ttf" }, true }, + { "ArialNarrow,BoldItalic", { "arialnbi.ttf" }, true }, + { "ArialNarrow-Bold", { "arialnb.ttf" }, true }, + { "ArialNarrow-Italic", { "arialni.ttf" }, true }, + { "ArialNarrow-BoldItalic", { "arialnbi.ttf" }, true }, + + { "HelveticaNarrow", { "arialn.ttf" }, true }, + { "HelveticaNarrow,Bold", { "arialnb.ttf" }, true }, + { "HelveticaNarrow,Italic", { "arialni.ttf" }, true }, + { "HelveticaNarrow,BoldItalic", { "arialnbi.ttf" }, true }, + { "HelveticaNarrow-Bold", { "arialnb.ttf" }, true }, + { "HelveticaNarrow-Italic", { "arialni.ttf" }, true }, + { "HelveticaNarrow-BoldItalic", { "arialnbi.ttf" }, true }, + + { "BookAntiqua", { "bkant.ttf" }, true }, + { "BookAntiqua,Bold", { "bkant.ttf" }, true }, + { "BookAntiqua,Italic", { "bkant.ttf" }, true }, + { "BookAntiqua,BoldItalic", { "bkant.ttf" }, true }, + { "BookAntiqua-Bold", { "bkant.ttf" }, true }, + { "BookAntiqua-Italic", { "bkant.ttf" }, true }, + { "BookAntiqua-BoldItalic", { "bkant.ttf" }, true }, + + { "Verdana", { "verdana.ttf" }, true }, + { "Verdana,Bold", { "verdanab.ttf" }, true }, + { "Verdana,Italic", { "verdanai.ttf" }, true }, + { "Verdana,BoldItalic", { "verdanaz.ttf" }, true }, + { "Verdana-Bold", { "verdanab.ttf" }, true }, + { "Verdana-Italic", { "verdanai.ttf" }, true }, + { "Verdana-BoldItalic", { "verdanaz.ttf" }, true }, + + { "Tahoma", { "tahoma.ttf" }, true }, + { "Tahoma,Bold", { "tahomabd.ttf" }, true }, + { "Tahoma,Italic", { "tahoma.ttf" }, true }, + { "Tahoma,BoldItalic", { "tahomabd.ttf" }, true }, + { "Tahoma-Bold", { "tahomabd.ttf" }, true }, + { "Tahoma-Italic", { "tahoma.ttf" }, true }, + { "Tahoma-BoldItalic", { "tahomabd.ttf" }, true }, + + { "CCRIKH+Verdana", { "verdana.ttf" }, true }, + { "CCRIKH+Verdana,Bold", { "verdanab.ttf" }, true }, + { "CCRIKH+Verdana,Italic", { "verdanai.ttf" }, true }, + { "CCRIKH+Verdana,BoldItalic", { "verdanaz.ttf" }, true }, + { "CCRIKH+Verdana-Bold", { "verdanab.ttf" }, true }, + { "CCRIKH+Verdana-Italic", { "verdanai.ttf" }, true }, + { "CCRIKH+Verdana-BoldItalic", { "verdanaz.ttf" }, true }, + + { "Georgia", { "georgia.ttf" }, true }, + { "Georgia,Bold", { "georgiab.ttf" }, true }, + { "Georgia,Italic", { "georgiai.ttf" }, true }, + { "Georgia,BoldItalic", { "georgiaz.ttf" }, true }, + { "Georgia-Bold", { "georgiab.ttf" }, true }, + { "Georgia-Italic", { "georgiai.ttf" }, true }, + { "Georgia-BoldItalic", { "georgiaz.ttf" }, true }, // fallback for Adobe CID fonts: - { "MingLiU", nullptr, "mingliu.ttf", false }, - { "SimSun", nullptr, "simsun.ttf", false }, - { "MS-Mincho", nullptr, "msmincho.ttf", false }, - { "Batang", nullptr, "batang.ttf", false }, - { "ArialUnicode", nullptr, "arialuni.ttf", true }, + { "MingLiU", { "mingliu.ttf" }, false }, + { "SimSun", { "simsun.ttf" }, false }, + { "MS-Mincho", { "msmincho.ttf" }, false }, + { "Batang", { "batang.ttf" }, false }, + { "ArialUnicode", { "arialuni.ttf" }, true }, {} }; static std::string GetWindowsFontDir() @@ -248,7 +247,6 @@ SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const c { int n; bool bold, italic, oblique, fixedWidth; - GooString *s; char c; int i; SysFontType type; @@ -274,7 +272,7 @@ SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const c } // remove trailing ' Oblique' - if (n > 7 && !strncmp(name + n - 8, " Oblique", 8)) { + if (n > 8 && !strncmp(name + n - 8, " Oblique", 8)) { n -= 8; oblique = true; } @@ -286,7 +284,7 @@ SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const c } // remove trailing ' Regular' - if (n > 5 && !strncmp(name + n - 8, " Regular", 8)) { + if (n > 8 && !strncmp(name + n - 8, " Regular", 8)) { n -= 8; } @@ -298,7 +296,7 @@ SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const c fixedWidth = false; //----- normalize the font name - s = new GooString(name, n); + std::unique_ptr s = std::make_unique(name, n); i = 0; while (i < s->getLength()) { c = s->getChar(i); @@ -315,7 +313,7 @@ SysFontInfo *SysFontList::makeWindowsFont(const char *name, int fontNum, const c type = sysFontTTF; } - return new SysFontInfo(s, bold, italic, oblique, fixedWidth, new GooString(path), type, fontNum, substituteName.copy()); + return new SysFontInfo(std::move(s), bold, italic, oblique, fixedWidth, std::make_unique(path), type, fontNum, substituteName.copy()); } static GooString *replaceSuffix(GooString *path, const char *suffixA, const char *suffixB) @@ -336,53 +334,55 @@ static GooString *replaceSuffix(GooString *path, const char *suffixA, const char void GlobalParams::setupBaseFonts(const char *dir) { - const char *dataRoot = popplerDataDir ? popplerDataDir : POPPLER_DATADIR; - GooString *fileName = nullptr; - if (baseFontsInitialized) return; baseFontsInitialized = true; const std::string winFontDir = GetWindowsFontDir(); + std::vector fontDirs; + if (dir) { + fontDirs.emplace_back(dir); + } + if (!winFontDir.empty()) { + fontDirs.emplace_back(winFontDir); + } + for (int i = 0; displayFontTab[i].name; ++i) { if (fontFiles.count(displayFontTab[i].name) > 0) continue; - GooString *fontName = new GooString(displayFontTab[i].name); + const GooString fontName = GooString(displayFontTab[i].name); - if (dir && displayFontTab[i].t1FileName) { - GooString *fontPath = appendToPath(new GooString(dir), displayFontTab[i].t1FileName); - if (FileExists(fontPath->c_str()) || FileExists(replaceSuffix(fontPath, ".pfb", ".pfa")->c_str())) { - addFontFile(fontName, fontPath); - continue; + bool fontFound = false; + for (const std::string &fontDir : fontDirs) { + for (const std::string &fileName : displayFontTab[i].fileNames) { + const std::unique_ptr fontPath(appendToPath(new GooString(fontDir), fileName.c_str())); + if (FileExists(fontPath->c_str()) || FileExists(replaceSuffix(fontPath.get(), ".pfb", ".pfa")->c_str()) || FileExists(replaceSuffix(fontPath.get(), ".ttc", ".ttf")->c_str())) { + addFontFile(fontName.toStr(), fontPath->toStr()); + fontFound = true; + break; + } } - delete fontPath; - } - if (!winFontDir.empty() && displayFontTab[i].ttFileName) { - GooString *fontPath = appendToPath(new GooString(winFontDir), displayFontTab[i].ttFileName); - if (FileExists(fontPath->c_str()) || FileExists(replaceSuffix(fontPath, ".ttc", ".ttf")->c_str())) { - addFontFile(fontName, fontPath); - continue; + if (fontFound) { + break; } - delete fontPath; } - if (displayFontTab[i].warnIfMissing) { + if (!fontFound && displayFontTab[i].warnIfMissing) { error(errSyntaxError, -1, "No display font for '{0:s}'", displayFontTab[i].name); - delete fontName; } } if (!winFontDir.empty()) { sysFonts->scanWindowsFonts(winFontDir); } - fileName = new GooString(dataRoot); - fileName->append("/cidfmap"); + std::string dataRoot = !popplerDataDir.empty() ? popplerDataDir : std::string { POPPLER_DATADIR }; + const std::string fileName = std::string(dataRoot).append("/cidfmap"); // try to open file - const std::unique_ptr file = GooFile::open(fileName->toStr()); + const std::unique_ptr file = GooFile::open(fileName); if (file) { Parser *parser; @@ -395,7 +395,7 @@ void GlobalParams::setupBaseFonts(const char *dir) if (obj2.isDict()) { Object obj3 = obj2.getDict()->lookup("Path"); if (obj3.isString()) - addFontFile(new GooString(obj1.getName()), obj3.getString()->copy()); + addFontFile(GooString(obj1.getName()).toStr(), obj3.getString()->toStr()); // Aliases } else if (obj2.isName()) { substFiles.emplace(obj1.getName(), obj2.getName()); @@ -408,8 +408,6 @@ void GlobalParams::setupBaseFonts(const char *dir) } } delete parser; - } else { - delete fileName; } } @@ -463,13 +461,13 @@ static const char *findSubstituteName(const GfxFont *font, const std::unordered_ } /* Windows implementation of external font matching code */ -GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) +std::optional GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName, const GooString *base14Name) { const SysFontInfo *fi; - GooString *path = nullptr; + std::string path; const std::optional &fontName = font->getName(); if (!fontName) - return nullptr; + return {}; const std::scoped_lock locker(mutex); setupBaseFonts(POPPLER_FONTSDIR); @@ -478,7 +476,7 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty // base14Name only for the creation of query pattern. if ((fi = sysFonts->find(*fontName, false, false))) { - path = fi->path->copy(); + path = fi->path->toStr(); *type = fi->type; *fontNum = fi->fontNum; if (substituteFontName) @@ -488,10 +486,10 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty error(errSyntaxError, -1, "Couldn't find a font for '{0:s}', subst is '{1:t}'", fontName->c_str(), substFontName); const auto fontFile = fontFiles.find(substFontName->toStr()); if (fontFile != fontFiles.end()) { - path = new GooString(fontFile->second.c_str()); + path = fontFile->second; if (substituteFontName) - substituteFontName->Set(path->c_str()); - if (!strcasecmp(path->c_str() + path->getLength() - 4, ".ttc")) { + substituteFontName->Set(path.c_str()); + if (path.ends_with(".ttc")) { *type = sysFontTTC; } else { *type = sysFontTTF; diff --git a/poppler/HashAlgorithm.h b/poppler/HashAlgorithm.h new file mode 100644 index 0000000..5253a76 --- /dev/null +++ b/poppler/HashAlgorithm.h @@ -0,0 +1,25 @@ +//======================================================================== +// +// HashAlgorithm.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#ifndef HASH_ALGORITHM_H +#define HASH_ALGORITHM_H + +enum class HashAlgorithm +{ + Unknown, + Md2, + Md5, + Sha1, + Sha256, + Sha384, + Sha512, + Sha224, +}; + +#endif // HASH_ALGORITHM_H diff --git a/poppler/Hints.cc b/poppler/Hints.cc index 4707e1c..4f5f004 100644 --- a/poppler/Hints.cc +++ b/poppler/Hints.cc @@ -12,6 +12,7 @@ // Copyright 2016 Jeffrey Morlan // Copyright 2019 LE GARREC Vincent // Copyright 2019 Adam Reichold +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -161,7 +162,7 @@ Hints::~Hints() gfree(sharedObjectId[i]); } } - gfree(sharedObjectId); + gfree(static_cast(sharedObjectId)); gfree(numSharedObject); gfree(groupLength); @@ -190,7 +191,10 @@ void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref if (hintsOffset && hintsLength) { std::unique_ptr s(str->makeSubStream(hintsOffset, false, hintsLength, Object(objNull))); - s->reset(); + if (!s->reset()) { + ok = false; + return; + } for (unsigned int i = 0; i < hintsLength; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { @@ -204,7 +208,10 @@ void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref if (hintsOffset2 && hintsLength2) { std::unique_ptr s(str->makeSubStream(hintsOffset2, false, hintsLength2, Object(objNull))); - s->reset(); + if (!s->reset()) { + ok = false; + return; + } for (unsigned int i = 0; i < hintsLength2; i++) { const int c = s->getChar(); if (unlikely(c == EOF)) { @@ -230,15 +237,20 @@ void Hints::readTables(BaseStream *str, Linearization *linearization, XRef *xref int sharedStreamOffset = 0; if (hintsDict->lookupInt("S", nullptr, &sharedStreamOffset) && sharedStreamOffset > 0) { - hintsStream->reset(); - ok = readPageOffsetTable(hintsStream); - - if (ok) { - hintsStream->reset(); - for (int i = 0; i < sharedStreamOffset; i++) { - hintsStream->getChar(); + if (!hintsStream->reset()) { + ok = false; + } else { + ok = readPageOffsetTable(hintsStream); + if (ok) { + if (hintsStream->reset()) { + for (int i = 0; i < sharedStreamOffset; i++) { + hintsStream->getChar(); + } + ok = readSharedObjectsTable(hintsStream); + } else { + ok = false; + } } - ok = readSharedObjectsTable(hintsStream); } } else { error(errSyntaxWarning, -1, "Invalid shared object hint table offset"); @@ -491,47 +503,6 @@ Goffset Hints::getPageOffset(int page) } } -std::vector *Hints::getPageRanges(int page) -{ - if ((page < 1) || (page > nPages)) { - return nullptr; - } - - int idx; - if (page - 1 > pageFirst) { - idx = page - 1; - } else if (page - 1 < pageFirst) { - idx = page; - } else { - idx = 0; - } - - ByteRange pageRange; - std::vector *v = new std::vector; - - pageRange.offset = pageOffset[idx]; - pageRange.length = pageLength[idx]; - v->push_back(pageRange); - - pageRange.offset = xRefOffset[idx]; - pageRange.length = 20 * nObjects[idx]; - v->push_back(pageRange); - - for (unsigned int j = 0; j < numSharedObject[idx]; j++) { - unsigned int k = sharedObjectId[idx][j]; - - pageRange.offset = groupOffset[k]; - pageRange.length = groupLength[k]; - v->push_back(pageRange); - - pageRange.offset = groupXRefOffset[k]; - pageRange.length = 20 * groupNumObjects[k]; - v->push_back(pageRange); - } - - return v; -} - int Hints::getPageObjectNum(int page) { if ((page < 1) || (page > nPages)) { diff --git a/poppler/Hints.h b/poppler/Hints.h index 6adbddc..5104ca0 100644 --- a/poppler/Hints.h +++ b/poppler/Hints.h @@ -39,7 +39,6 @@ class Hints int getPageObjectNum(int page); Goffset getPageOffset(int page); - std::vector *getPageRanges(int page); private: void readTables(BaseStream *str, Linearization *linearization, XRef *xref, SecurityHandler *secHdlr); diff --git a/poppler/ImageEmbeddingUtils.cc b/poppler/ImageEmbeddingUtils.cc index 0c13fe9..ffaa3a9 100644 --- a/poppler/ImageEmbeddingUtils.cc +++ b/poppler/ImageEmbeddingUtils.cc @@ -5,6 +5,8 @@ // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2021, 2022 Albert Astals Cid // Copyright (C) 2021 Marco Genasci +// Copyright (C) 2023 Jordan Abrahams-Whitehead +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // This file is licensed under the GPLv2 or later // @@ -14,6 +16,7 @@ #include #ifdef ENABLE_LIBJPEG +# include extern "C" { # include } @@ -285,10 +288,10 @@ Ref PngEmbedder::embedImage(XRef *xref) Dict *baseImageDict = createImageDict(xref, colorSpace, m_width, m_height, m_bitDepth); if (m_hasAlpha) { Dict *maskImageDict = createImageDict(xref, DEVICE_GRAY, m_width, m_height, m_bitDepth); - Ref maskImageRef = xref->addStreamObject(maskImageDict, maskBuffer, maskBufferSize); + Ref maskImageRef = xref->addStreamObject(maskImageDict, maskBuffer, maskBufferSize, StreamCompression::Compress); baseImageDict->add("SMask", Object(maskImageRef)); } - return xref->addStreamObject(baseImageDict, mainBuffer, mainBufferSize); + return xref->addStreamObject(baseImageDict, mainBuffer, mainBufferSize, StreamCompression::Compress); } #endif @@ -358,7 +361,7 @@ Ref JpegEmbedder::embedImage(XRef *xref) } Dict *baseImageDict = createImageDict(xref, DEVICE_RGB, m_width, m_height, 8); baseImageDict->add("Filter", Object(objName, "DCTDecode")); - Ref baseImageRef = xref->addStreamObject(baseImageDict, m_fileContent.release(), m_fileSize); + Ref baseImageRef = xref->addStreamObject(baseImageDict, m_fileContent.release(), m_fileSize, StreamCompression::None); return baseImageRef; } #endif diff --git a/poppler/JBIG2Stream.cc b/poppler/JBIG2Stream.cc index 77ffeb2..cf9e0c9 100644 --- a/poppler/JBIG2Stream.cc +++ b/poppler/JBIG2Stream.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2006 Raj Kumar // Copyright (C) 2006 Paul Walmsley -// Copyright (C) 2006-2010, 2012, 2014-2022 Albert Astals Cid +// Copyright (C) 2006-2010, 2012, 2014-2022, 2024 Albert Astals Cid // Copyright (C) 2009 David Benjamin // Copyright (C) 2011 Edward Jiang // Copyright (C) 2012 William Bader @@ -28,6 +28,8 @@ // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 Volker Krause // Copyright (C) 2019-2021 Even Rouault +// Copyright (C) 2024, 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -184,7 +186,7 @@ class JBIG2HuffmanDecoder { public: JBIG2HuffmanDecoder(); - ~JBIG2HuffmanDecoder(); + ~JBIG2HuffmanDecoder() = default; void setStream(Stream *strA) { str = strA; } void reset(); @@ -215,8 +217,6 @@ JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() reset(); } -JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() { } - void JBIG2HuffmanDecoder::reset() { buf = 0; @@ -351,7 +351,7 @@ class JBIG2MMRDecoder { public: JBIG2MMRDecoder(); - ~JBIG2MMRDecoder(); + ~JBIG2MMRDecoder() = default; void setStream(Stream *strA) { str = strA; } void reset(); int get2DCode(); @@ -377,8 +377,6 @@ JBIG2MMRDecoder::JBIG2MMRDecoder() reset(); } -JBIG2MMRDecoder::~JBIG2MMRDecoder() { } - void JBIG2MMRDecoder::reset() { buf = 0; @@ -765,7 +763,7 @@ void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp) { - int x0, x1, y0, y1, xx, yy; + int x0, x1, y0, y1, xx, yy, yyy; unsigned char *srcPtr, *destPtr; unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3; bool oneByte; @@ -812,14 +810,17 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp oneByte = x0 == ((x1 - 1) & ~7); for (yy = y0; yy < y1; ++yy) { - if (unlikely((y + yy >= h) || (y + yy < 0))) { + if (unlikely(checkedAdd(y, yy, &yyy))) { + continue; + } + if (unlikely((yyy >= h) || (yyy < 0))) { continue; } // one byte per line -- need to mask both left and right side if (oneByte) { if (x >= 0) { - destPtr = data + (y + yy) * line + (x >> 3); + destPtr = data + yyy * line + (x >> 3); srcPtr = bitmap->data + yy * bitmap->line; dest = *destPtr; src1 = *srcPtr; @@ -842,7 +843,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp } *destPtr = dest; } else { - destPtr = data + (y + yy) * line; + destPtr = data + yyy * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); dest = *destPtr; src1 = *srcPtr; @@ -872,7 +873,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp // left-most byte if (x >= 0) { - destPtr = data + (y + yy) * line + (x >> 3); + destPtr = data + yyy * line + (x >> 3); srcPtr = bitmap->data + yy * bitmap->line; src1 = *srcPtr++; dest = *destPtr; @@ -896,7 +897,7 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp *destPtr++ = dest; xx = x0 + 8; } else { - destPtr = data + (y + yy) * line; + destPtr = data + yyy * line; srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); src1 = *srcPtr++; xx = x0; @@ -1013,13 +1014,9 @@ JBIG2SymbolDict::~JBIG2SymbolDict() for (i = 0; i < size; ++i) { delete bitmaps[i]; } - gfree(bitmaps); - if (genericRegionStats) { - delete genericRegionStats; - } - if (refinementRegionStats) { - delete refinementRegionStats; - } + gfree(static_cast(bitmaps)); + delete genericRegionStats; + delete refinementRegionStats; } //------------------------------------------------------------------------ @@ -1064,7 +1061,7 @@ JBIG2PatternDict::~JBIG2PatternDict() for (i = 0; i < size; ++i) { delete bitmaps[i]; } - gfree(bitmaps); + gfree(static_cast(bitmaps)); } //------------------------------------------------------------------------ @@ -1157,15 +1154,16 @@ JBIG2Stream::~JBIG2Stream() delete str; } -void JBIG2Stream::reset() +bool JBIG2Stream::reset() { segments.resize(0); globalSegments.resize(0); + bool innerReset = true; // read the globals stream if (globalsStream.isStream()) { curStr = globalsStream.getStream(); - curStr->reset(); + innerReset = innerReset && curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); @@ -1177,7 +1175,7 @@ void JBIG2Stream::reset() // read the main stream curStr = str; - curStr->reset(); + innerReset = innerReset && curStr->reset(); arithDecoder->setStream(curStr); huffDecoder->setStream(curStr); mmrDecoder->setStream(curStr); @@ -1189,6 +1187,8 @@ void JBIG2Stream::reset() } else { dataPtr = dataEnd = nullptr; } + + return innerReset; } void JBIG2Stream::close() @@ -1245,9 +1245,9 @@ int JBIG2Stream::getChars(int nChars, unsigned char *buffer) return n; } -GooString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) +std::optional JBIG2Stream::getPSFilter(int psLevel, const char *indent) { - return nullptr; + return {}; } bool JBIG2Stream::isBinary(bool last) const @@ -1803,6 +1803,7 @@ bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length, un } *p++ = (unsigned char)c; } + byteCounter += k; } else { collBitmap = readGenericBitmap(true, totalWidth, symHeight, 0, false, false, nullptr, nullptr, nullptr, bmSize).release(); } @@ -1864,7 +1865,7 @@ bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length, un for (i = 0; i < numNewSyms; ++i) { delete bitmaps[numInputSyms + i]; } - gfree(bitmaps); + gfree(static_cast(bitmaps)); if (symWidths) { gfree(symWidths); } @@ -1891,7 +1892,7 @@ bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length, un delete bitmaps[numInputSyms + i]; } } - gfree(bitmaps); + gfree(static_cast(bitmaps)); if (symWidths) { gfree(symWidths); } @@ -2136,7 +2137,7 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless if (huff) { symCodeTab = (JBIG2HuffmanTable *)gmallocn_checkoverflow(numSyms + 1, sizeof(JBIG2HuffmanTable)); if (!symCodeTab) { - gfree(syms); + gfree(static_cast(syms)); return; } for (i = 0; i < numSyms; ++i) { @@ -2177,7 +2178,7 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless if (!huff) { if (!resetIntStats(symCodeLen)) { - gfree(syms); + gfree(static_cast(syms)); return; } arithDecoder->start(); @@ -2189,7 +2190,7 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless bitmap = readTextRegion(huff, refine, w, h, numInstances, logStrips, numSyms, symCodeTab, symCodeLen, syms, defPixel, combOp, transposed, refCorner, sOffset, huffFSTable, huffDSTable, huffDTTable, huffRDWTable, huffRDHTable, huffRDXTable, huffRDYTable, huffRSizeTable, templ, atx, aty); - gfree(syms); + gfree(static_cast(syms)); if (bitmap) { // combine the region bitmap into the page bitmap @@ -2217,12 +2218,11 @@ void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, bool lossless codeTableError: error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 text region"); - gfree(syms); + gfree(static_cast(syms)); return; eofError: error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); - return; } std::unique_ptr JBIG2Stream::readTextRegion(bool huff, bool refine, int w, int h, unsigned int numInstances, unsigned int logStrips, int numSyms, const JBIG2HuffmanTable *symCodeTab, unsigned int symCodeLen, JBIG2Bitmap **syms, diff --git a/poppler/JBIG2Stream.h b/poppler/JBIG2Stream.h index af63a67..4b85461 100644 --- a/poppler/JBIG2Stream.h +++ b/poppler/JBIG2Stream.h @@ -20,6 +20,8 @@ // Copyright (C) 2019 Volker Krause // Copyright (C) 2019, 2021 Albert Astals Cid // Copyright (C) 2019, 2020 Even Rouault +// Copyright (C) 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -48,12 +50,12 @@ class JBIG2Stream : public FilterStream JBIG2Stream(Stream *strA, Object &&globalsStreamA, Object *globalsStreamRefA); ~JBIG2Stream() override; StreamKind getKind() const override { return strJBIG2; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; Goffset getPos() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; virtual Object *getGlobalsStream() { return &globalsStream; } virtual Ref getGlobalsStreamRef() { return globalsStreamRef; } diff --git a/poppler/JPEG2000Stream.cc b/poppler/JPEG2000Stream.cc index c480ba2..455eba6 100644 --- a/poppler/JPEG2000Stream.cc +++ b/poppler/JPEG2000Stream.cc @@ -4,13 +4,15 @@ // // A JPX stream decoder using OpenJPEG // -// Copyright 2008-2010, 2012, 2017-2022 Albert Astals Cid +// Copyright 2008-2010, 2012, 2017-2023 Albert Astals Cid // Copyright 2011 Daniel Glöckner // Copyright 2014, 2016 Thomas Freitag // Copyright 2013, 2014 Adrian Johnson // Copyright 2015 Adam Reichold // Copyright 2015 Jakub Wilk // Copyright 2022 Oliver Sander +// Copyright 2024, 2025 Nelson Benítez León +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // Licensed under GPLv2 or later // @@ -20,17 +22,6 @@ #include "JPEG2000Stream.h" #include -#define OPENJPEG_VERSION_ENCODE(major, minor, micro) (((major)*10000) + ((minor)*100) + ((micro)*1)) - -#ifdef OPJ_VERSION_MAJOR -# define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(OPJ_VERSION_MAJOR, OPJ_VERSION_MINOR, OPJ_VERSION_BUILD) -#else -// OpenJPEG started providing version macros in version 2.1. -// If the version macro is not found, set the version to 2.0.0 and -// assume there will be no API changes in 2.0.x. -# define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(2, 0, 0) -#endif - struct JPXStreamPrivate { opj_image_t *image = nullptr; @@ -80,6 +71,7 @@ static inline int doGetChar(JPXStreamPrivate *priv) JPXStream::JPXStream(Stream *strA) : FilterStream(strA) { priv = new JPXStreamPrivate; + handleJPXtransparency = false; } JPXStream::~JPXStream() @@ -89,10 +81,12 @@ JPXStream::~JPXStream() delete priv; } -void JPXStream::reset() +bool JPXStream::reset() { priv->counter = 0; priv->ccounter = 0; + + return true; } void JPXStream::close() @@ -144,9 +138,9 @@ int JPXStream::lookChar() return doLookChar(priv); } -GooString *JPXStream::getPSFilter(int psLevel, const char *indent) +std::optional JPXStream::getPSFilter(int psLevel, const char *indent) { - return nullptr; + return {}; } bool JPXStream::isBinary(bool last) const @@ -154,22 +148,26 @@ bool JPXStream::isBinary(bool last) const return str->isBinary(true); } -void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) +void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode, bool *hasAlpha) { if (unlikely(priv->inited == false)) { init(); } *bitsPerComponent = 8; + *hasAlpha = false; int numComps = (priv->image) ? priv->image->numcomps : 1; if (priv->image) { if (priv->image->color_space == OPJ_CLRSPC_SRGB && numComps == 4) { numComps = 3; + *hasAlpha = true; } else if (priv->image->color_space == OPJ_CLRSPC_SYCC && numComps == 4) { numComps = 3; + *hasAlpha = true; } else if (numComps == 2) { numComps = 1; } else if (numComps > 4) { + *hasAlpha = true; numComps = 4; } } @@ -283,7 +281,7 @@ void JPXStream::init() } priv->npixels = priv->image->comps[0].w * priv->image->comps[0].h; priv->ncomps = priv->image->numcomps; - if (alpha == 1 && smaskInData == 0) { + if (alpha == 1 && smaskInData == 0 && !supportJPXtransparency()) { priv->ncomps--; } for (int component = 0; component < priv->ncomps; component++) { @@ -333,11 +331,7 @@ void JPXStreamPrivate::init2(OPJ_CODEC_FORMAT format, const unsigned char *buf, stream = opj_stream_default_create(OPJ_TRUE); -#if OPENJPEG_VERSION >= OPENJPEG_VERSION_ENCODE(2, 1, 0) opj_stream_set_user_data(stream, &jpxData, nullptr); -#else - opj_stream_set_user_data(stream, &jpxData); -#endif opj_stream_set_read_function(stream, jpxRead_callback); opj_stream_set_skip_function(stream, jpxSkip_callback); diff --git a/poppler/JPEG2000Stream.h b/poppler/JPEG2000Stream.h index fbaac8f..e1c44de 100644 --- a/poppler/JPEG2000Stream.h +++ b/poppler/JPEG2000Stream.h @@ -8,6 +8,8 @@ // Copyright 2011 Daniel Glöckner // Copyright 2013, 2014 Adrian Johnson // Copyright 2015 Adam Reichold +// Copyright 2024, 2025 Nelson Benítez León +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // Licensed under GPLv2 or later // @@ -32,19 +34,26 @@ class JPXStream : public FilterStream JPXStream &operator=(const JPXStream &other) = delete; StreamKind getKind() const override { return strJPX; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; Goffset getPos() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; - void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) override; + void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode, bool *hasAlpha) override; + + // Whether this JPX Stream should handle transparency (usually set when OutputDev also supports it) + void setSupportJPXtransparency(bool val) { handleJPXtransparency = val; } + + // Does this JPX stream support transparency (alpha channel)? + bool supportJPXtransparency() { return handleJPXtransparency; } int readStream(int nChars, unsigned char *buffer) { return str->doGetChars(nChars, buffer); } private: JPXStreamPrivate *priv; + bool handleJPXtransparency; void init(); bool hasGetChars() override { return true; } diff --git a/poppler/JPXStream.cc b/poppler/JPXStream.cc index b22018e..1da2b10 100644 --- a/poppler/JPXStream.cc +++ b/poppler/JPXStream.cc @@ -17,6 +17,8 @@ // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2012 Even Rouault // Copyright (C) 2019 Robert Niemi +// Copyright (C) 2024, 2025 Nelson Benítez León +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -26,7 +28,7 @@ #include #include -#include "gmem.h" +#include "goo/gmem.h" #include "Error.h" #include "JArithmeticDecoder.h" #include "JPXStream.h" @@ -178,7 +180,7 @@ static const unsigned int signContext[5][5][2] = { #define jpxFloorDivPow2(x, y) ((x) >> (y)) // ceil(x / y) -#define jpxCeilDiv(x, y) (((x) + (y)-1) / (y)) +#define jpxCeilDiv(x, y) (((x) + (y) - 1) / (y)) // ceil(x / 2^y) #define jpxCeilDivPow2(x, y) (((x) + (1 << (y)) - 1) >> (y)) @@ -269,18 +271,21 @@ JPXStream::~JPXStream() delete bufStr; } -void JPXStream::reset() +bool JPXStream::reset() { - bufStr->reset(); + bool ret = bufStr->reset(); if (readBoxes()) { curY = img.yOffset; } else { // readBoxes reported an error, so we go immediately to EOF curY = img.ySize; + ret = false; } curX = img.xOffset; curComp = 0; readBufLen = 0; + + return ret; } void JPXStream::close() @@ -471,9 +476,9 @@ void JPXStream::fillReadBuf() } while (readBufLen < 8); } -GooString *JPXStream::getPSFilter(int psLevel, const char *indent) +std::optional JPXStream::getPSFilter(int psLevel, const char *indent) { - return nullptr; + return {}; } bool JPXStream::isBinary(bool last) const @@ -481,7 +486,7 @@ bool JPXStream::isBinary(bool last) const return str->isBinary(true); } -void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) +void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode, bool *hasAlpha) { unsigned int boxType, boxLen, dataLen, csEnum; unsigned int bpc1, dummy, i; @@ -490,8 +495,9 @@ void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMo bool haveBPC, haveCSMode; csPrec = 0; // make gcc happy + *hasAlpha = false; haveBPC = haveCSMode = false; - bufStr->reset(); + (void)bufStr->reset(); if (bufStr->lookChar() == 0xff) { getImageParams2(bitsPerComponent, csMode); } else { diff --git a/poppler/JPXStream.h b/poppler/JPXStream.h index d177ac6..f2fdaa1 100644 --- a/poppler/JPXStream.h +++ b/poppler/JPXStream.h @@ -14,6 +14,8 @@ // under GPL version 2 or later // // Copyright (C) 2019, 2021 Albert Astals Cid +// Copyright (C) 2024, 2025 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -287,13 +289,13 @@ class JPXStream : public FilterStream JPXStream(Stream *strA); virtual ~JPXStream(); StreamKind getKind() const override { return strJPX; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; - void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) override; + void getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode, bool *hasAlpha) override; private: void fillReadBuf(); diff --git a/poppler/JSInfo.cc b/poppler/JSInfo.cc index 29fa707..4aff120 100644 --- a/poppler/JSInfo.cc +++ b/poppler/JSInfo.cc @@ -5,10 +5,11 @@ // This file is licensed under the GPLv2 or later // // Copyright (C) 2013 Adrian Johnson -// Copyright (C) 2017, 2020, 2021 Albert Astals Cid +// Copyright (C) 2017, 2020, 2021, 2024 Albert Astals Cid // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2020 Oliver Sander // Copyright (C) 2020 Nelson Benítez León +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -34,24 +35,19 @@ JSInfo::JSInfo(PDFDoc *docA, int firstPage) currentPage = firstPage + 1; } -JSInfo::~JSInfo() { } - -void JSInfo::printJS(const GooString *js) +void JSInfo::printJS(std::string_view js) { - Unicode *u = nullptr; char buf[8]; - int i, n, len; - if (!js || !js->c_str()) { + if (js.empty()) { return; } - len = TextStringToUCS4(js->toStr(), &u); - for (i = 0; i < len; i++) { - n = uniMap->mapUnicode(u[i], buf, sizeof(buf)); + std::vector u = TextStringToUCS4(js); + for (auto &c : u) { + int n = uniMap->mapUnicode(c, buf, sizeof(buf)); fwrite(buf, 1, n, file); } - gfree(u); } void JSInfo::scanLinkAction(LinkAction *link, const char *action) @@ -67,8 +63,7 @@ void JSInfo::scanLinkAction(LinkAction *link, const char *action) if (linkjs->isOk()) { const std::string &s = linkjs->getScript(); fprintf(file, "%s:\n", action); - GooString gooS = GooString(s); - printJS(&gooS); + printJS(s); fputs("\n\n", file); } } @@ -80,8 +75,8 @@ void JSInfo::scanLinkAction(LinkAction *link, const char *action) hasJS = true; if (print) { fprintf(file, "%s (Rendition):\n", action); - const GooString s(linkr->getScript()); - printJS(&s); + const std::string &s(linkr->getScript()); + printJS(s); fputs("\n\n", file); } } @@ -131,15 +126,15 @@ void JSInfo::scan(int nPages) if (print) { for (int i = 0; i < numNames; i++) { fprintf(file, "Name Dictionary \"%s\":\n", doc->getCatalog()->getJSName(i)->c_str()); - GooString *js = doc->getCatalog()->getJS(i); + std::string js = doc->getCatalog()->getJS(i); printJS(js); - delete js; fputs("\n\n", file); } } } // document actions + scanLinkAction(doc->getCatalog()->getOpenAction().get(), "Open Document Action"); scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionCloseDocument).get(), "Before Close Document"); scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentStart).get(), "Before Save Document"); scanLinkAction(doc->getCatalog()->getAdditionalAction(Catalog::actionSaveDocumentFinish).get(), "After Save Document"); diff --git a/poppler/JSInfo.h b/poppler/JSInfo.h index cfd3a0d..42d8bd0 100644 --- a/poppler/JSInfo.h +++ b/poppler/JSInfo.h @@ -9,6 +9,7 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2020 Oliver Sander // Copyright (C) 2020 Nelson Benítez León +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -34,7 +35,7 @@ class POPPLER_PRIVATE_EXPORT JSInfo explicit JSInfo(PDFDoc *doc, int firstPage = 0); // Destructor. - ~JSInfo(); + ~JSInfo() = default; // scan for JS in the PDF void scanJS(int nPages); @@ -59,7 +60,7 @@ class POPPLER_PRIVATE_EXPORT JSInfo void scan(int nPages); void scanLinkAction(LinkAction *link, const char *action); - void printJS(const GooString *js); + void printJS(std::string_view js); }; #endif diff --git a/poppler/Lexer.cc b/poppler/Lexer.cc index ab25caf..cc414d4 100644 --- a/poppler/Lexer.cc +++ b/poppler/Lexer.cc @@ -13,11 +13,14 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2006-2010, 2012-2014, 2017-2019 Albert Astals Cid +// Copyright (C) 2006-2010, 2012-2014, 2017-2019, 2024 Albert Astals Cid // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2010 Carlos Garcia Campos // Copyright (C) 2012, 2013 Adrian Johnson // Copyright (C) 2013 Thomas Freitag +// Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2023 Even Rouault +// Copyright (C) 2023 Oliver Sander // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -33,6 +36,7 @@ #include #include "Lexer.h" #include "Error.h" +#include "UTF.h" #include "XRef.h" //------------------------------------------------------------------------ @@ -75,7 +79,7 @@ Lexer::Lexer(XRef *xrefA, Stream *str) streams->add(curStr.copy()); strPtr = 0; freeArray = true; - curStr.streamReset(); + (void)curStr.streamReset(); } Lexer::Lexer(XRef *xrefA, Object *obj) @@ -95,7 +99,7 @@ Lexer::Lexer(XRef *xrefA, Object *obj) if (streams->getLength() > 0) { curStr = streams->get(strPtr); if (curStr.isStream()) { - curStr.streamReset(); + (void)curStr.streamReset(); } } } @@ -131,7 +135,9 @@ int Lexer::getChar(bool comesFromLook) if (strPtr < streams->getLength()) { curStr = streams->get(strPtr); if (curStr.isStream()) { - curStr.streamReset(); + if (!curStr.streamReset()) { + return EOF; + } } } } @@ -163,7 +169,6 @@ Object Lexer::getObj(int objNum) int xi; long long xll = 0; double xf = 0, scale; - GooString *s; int n, m; // skip whitespace and comments @@ -288,12 +293,12 @@ Object Lexer::getObj(int objNum) break; // string - case '(': + case '(': { p = tokBuf; n = 0; numParen = 1; done = false; - s = nullptr; + std::string s; do { c2 = EOF; switch (c = getChar()) { @@ -388,11 +393,7 @@ Object Lexer::getObj(int objNum) if (c2 != EOF) { if (n == tokBufSize) { - if (!s) { - s = new GooString(tokBuf, tokBufSize); - } else { - s->append(tokBuf, tokBufSize); - } + s.append(tokBuf, tokBufSize); p = tokBuf; n = 0; @@ -402,7 +403,7 @@ Object Lexer::getObj(int objNum) if (newObjNum != objNum) { error(errSyntaxError, getPos(), "Unterminated string"); done = true; - delete s; + s.clear(); n = -2; } } @@ -412,22 +413,22 @@ Object Lexer::getObj(int objNum) } } while (!done); if (n >= 0) { - if (!s) { - s = new GooString(tokBuf, n); - } else { - s->append(tokBuf, n); + s.append(tokBuf, n); + // Check utf8 + if (isUtf8WithBom(s)) { + s = utf8ToUtf16WithBom(s); } - return Object(s); + return Object(std::move(s)); } else { return Object(objEOF); } break; - + } // name - case '/': + case '/': { p = tokBuf; n = 0; - s = nullptr; + std::string s; while ((c = lookChar()) != EOF && !specialChars[c]) { getChar(); if (c == '#') { @@ -464,20 +465,25 @@ Object Lexer::getObj(int objNum) } else if (n == tokBufSize) { error(errSyntaxError, getPos(), "Warning: name token is longer than what the specification says it can be"); *p = c; - s = new GooString(tokBuf, n); + s = std::string(tokBuf, n); } else { - s->append((char)c); + // Somewhat arbitrary threshold + if (unlikely(n == 1024 * 1024)) { + error(errSyntaxError, getPos(), "Error: name token is larger than 1 MB. Suspicion of hostile file. Stopping parsing"); + return Object(objEOF); + } + s.push_back((char)c); } } if (n < tokBufSize) { *p = '\0'; return Object(objName, tokBuf); } else { - Object obj(objName, s->c_str()); - delete s; + Object obj(objName, s.c_str()); return obj; } break; + } // array punctuation case '[': @@ -503,7 +509,7 @@ Object Lexer::getObj(int objNum) p = tokBuf; m = n = 0; c2 = 0; - s = nullptr; + std::string s; while (true) { c = getChar(); if (c == '>') { @@ -524,11 +530,7 @@ Object Lexer::getObj(int objNum) } if (++m == 2) { if (n == tokBufSize) { - if (!s) { - s = new GooString(tokBuf, tokBufSize); - } else { - s->append(tokBuf, tokBufSize); - } + s.append(tokBuf, tokBufSize); p = tokBuf; n = 0; } @@ -539,15 +541,14 @@ Object Lexer::getObj(int objNum) } } } - if (!s) { - s = new GooString(tokBuf, n); - } else { - s->append(tokBuf, n); - } + s.append(tokBuf, n); if (m == 1) { - s->append((char)(c2 << 4)); + s.push_back((char)(c2 << 4)); + } + if (isUtf8WithBom(s)) { + s = utf8ToUtf16WithBom(s); } - return Object(s); + return Object(std::move(s)); } break; @@ -613,7 +614,7 @@ Object Lexer::getObj(const char *cmdA, int objNum) comment = false; const char *cmd1 = tokBuf; *tokBuf = 0; - while (strcmp(cmdA, cmd1) && (objNum < 0 || (xref && xref->getNumEntry(getPos()) == objNum))) { + while ((strcmp(cmdA, cmd1) != 0) && (objNum < 0 || (xref && xref->getNumEntry(getPos()) == objNum))) { while (true) { if ((c = getChar()) == EOF) { return Object(objEOF); diff --git a/poppler/Linearization.cc b/poppler/Linearization.cc index 7cf9eda..0fc7ada 100644 --- a/poppler/Linearization.cc +++ b/poppler/Linearization.cc @@ -9,6 +9,7 @@ // Copyright 2017, 2019 Albert Astals Cid // Copyright 2019 Adam Reichold // Copyright 2019 Even Rouault +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -24,7 +25,7 @@ Linearization::Linearization(BaseStream *str) { Parser *parser; - str->reset(); + (void)str->reset(); parser = new Parser(nullptr, str->makeSubStream(str->getStart(), false, 0, Object(objNull)), false); Object obj1 = parser->getObj(); Object obj2 = parser->getObj(); @@ -41,7 +42,7 @@ Linearization::Linearization(BaseStream *str) delete parser; } -Linearization::~Linearization() { } +Linearization::~Linearization() = default; unsigned int Linearization::getLength() const { diff --git a/poppler/Link.cc b/poppler/Link.cc index 8aca0b9..bbc4d8d 100644 --- a/poppler/Link.cc +++ b/poppler/Link.cc @@ -16,7 +16,7 @@ // Copyright (C) 2006, 2008 Pino Toscano // Copyright (C) 2007, 2010, 2011 Carlos Garcia Campos // Copyright (C) 2008 Hugo Mercier -// Copyright (C) 2008-2010, 2012-2014, 2016-2022 Albert Astals Cid +// Copyright (C) 2008-2010, 2012-2014, 2016-2024 Albert Astals Cid // Copyright (C) 2009 Kovid Goyal // Copyright (C) 2009 Ilya Gorenbein // Copyright (C) 2012 Tobias Koening @@ -25,6 +25,8 @@ // Copyright (C) 2018, 2020 Adam Reichold // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2020 Marek Kasik +// Copyright (C) 2024 Pratham Gandhi +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -134,6 +136,10 @@ std::unique_ptr LinkAction::parseAction(const Object *obj, const std } else if (obj2.isName("ResetForm")) { action = std::make_unique(obj); + // SubmitForm action + } else if (obj2.isName("SubmitForm")) { + action = std::make_unique(obj); + // unknown action } else if (obj2.isName()) { action = std::make_unique(obj2.getName()); @@ -418,7 +424,7 @@ LinkGoTo::LinkGoTo(const Object *destObj) if (destObj->isName()) { namedDest = std::make_unique(destObj->getName()); } else if (destObj->isString()) { - namedDest = std::unique_ptr(destObj->getString()->copy()); + namedDest = destObj->getString()->copy(); // destination dictionary } else if (destObj->isArray()) { @@ -444,14 +450,14 @@ LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) // get file name Object obj1 = getFileSpecNameForPlatform(fileSpecObj); if (obj1.isString()) { - fileName = std::unique_ptr(obj1.getString()->copy()); + fileName = obj1.getString()->copy(); } // named destination if (destObj->isName()) { namedDest = std::make_unique(destObj->getName()); } else if (destObj->isString()) { - namedDest = std::unique_ptr(destObj->getString()->copy()); + namedDest = destObj->getString()->copy(); // destination dictionary } else if (destObj->isArray()) { @@ -480,7 +486,7 @@ LinkLaunch::LinkLaunch(const Object *actionObj) if (!obj1.isNull()) { Object obj3 = getFileSpecNameForPlatform(&obj1); if (obj3.isString()) { - fileName = std::unique_ptr(obj3.getString()->copy()); + fileName = obj3.getString()->copy(); } } else { #ifdef _WIN32 @@ -494,11 +500,11 @@ LinkLaunch::LinkLaunch(const Object *actionObj) Object obj2 = obj1.dictLookup("F"); Object obj3 = getFileSpecNameForPlatform(&obj2); if (obj3.isString()) { - fileName = std::unique_ptr(obj3.getString()->copy()); + fileName = obj3.getString()->copy(); } obj2 = obj1.dictLookup("P"); if (obj2.isString()) { - params = std::unique_ptr(obj2.getString()->copy()); + params = obj2.getString()->copy(); } } else { error(errSyntaxWarning, -1, "Bad launch-type link action"); @@ -530,7 +536,7 @@ LinkURI::LinkURI(const Object *uriObj, const std::optional &baseURI // relative URI if (baseURI) { uri = *baseURI; - if (uri.size() > 0) { + if (!uri.empty()) { char c = uri.back(); if (c != '/' && c != '?') { uri += '/'; @@ -714,7 +720,7 @@ LinkRendition::LinkRendition(const Object *obj) operation = PlayRendition; break; } - } else if (js == "") { + } else if (js.empty()) { error(errSyntaxWarning, -1, "Invalid Rendition action: no OP or JS field defined"); } } @@ -745,11 +751,11 @@ LinkJavaScript::LinkJavaScript(Object *jsObj) LinkJavaScript::~LinkJavaScript() = default; -Object LinkJavaScript::createObject(XRef *xref, const GooString &js) +Object LinkJavaScript::createObject(XRef *xref, const std::string &js) { Dict *linkDict = new Dict(xref); linkDict->add("S", Object(objName, "JavaScript")); - linkDict->add("JS", Object(js.copy())); + linkDict->add("JS", Object(std::make_unique(js))); return Object(linkDict); } @@ -869,6 +875,55 @@ LinkResetForm::LinkResetForm(const Object *obj) LinkResetForm::~LinkResetForm() = default; +//------------------------------------------------------------------------ +// LinkSubmitForm +//------------------------------------------------------------------------ + +LinkSubmitForm::LinkSubmitForm(const Object *obj) +{ + if (!obj->isDict()) { + return; + } + + const Object objFields = obj->dictLookup("Fields"); + if (objFields.isArray()) { + fields.resize(objFields.arrayGetLength()); + for (int i = 0; i < objFields.arrayGetLength(); ++i) { + const Object &objNF = objFields.arrayGetNF(i); + if (objNF.isName()) { + fields[i] = std::string(objNF.getName()); + } else if (objNF.isString()) { + fields[i] = objNF.getString()->toStr(); + } else if (objNF.isRef()) { + fields[i] = std::to_string(objNF.getRef().num); + fields[i].append(" "); + fields[i].append(std::to_string(objNF.getRef().gen)); + fields[i].append(" R"); + } else { + error(errSyntaxWarning, -1, "LinkSubmitForm: unexpected Field type"); + } + } + } + + Object objFileSpecification = obj->dictLookup("F"); + if (objFileSpecification.isDict()) { + objFileSpecification = objFileSpecification.dictLookup("F"); + if (objFileSpecification.isString()) { + url = objFileSpecification.getString()->toStr(); + } + } else if (objFileSpecification.isString()) { + url = objFileSpecification.getString()->toStr(); + } + + const Object objFlags = obj->dictLookup("Flags"); + if (objFlags.isInt()) { + flags = objFlags.getInt(); + } + // It is the responsibility of the frontend to remove fields that have NoExport flag set. +} + +LinkSubmitForm::~LinkSubmitForm() = default; + //------------------------------------------------------------------------ // LinkUnknown //------------------------------------------------------------------------ diff --git a/poppler/Link.h b/poppler/Link.h index 204207b..b1cd5c4 100644 --- a/poppler/Link.h +++ b/poppler/Link.h @@ -17,12 +17,13 @@ // Copyright (C) 2008 Hugo Mercier // Copyright (C) 2010, 2011 Carlos Garcia Campos // Copyright (C) 2012 Tobias Koening -// Copyright (C) 2018-2022 Albert Astals Cid +// Copyright (C) 2018-2023 Albert Astals Cid // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Intevation GmbH // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2020 Adam Reichold // Copyright (C) 2020 Marek Kasik +// Copyright (C) 2024 Pratham Gandhi // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -64,6 +65,7 @@ enum LinkActionKind actionOCGState, // Set-OCG-State action actionHide, // Hide action actionResetForm, // ResetForm action + actionSubmitForm, // SubmitForm action actionUnknown // anything else }; @@ -412,7 +414,7 @@ class LinkJavaScript : public LinkAction LinkActionKind getKind() const override { return actionJavaScript; } const std::string &getScript() const { return js; } - static Object createObject(XRef *xref, const GooString &js); + static Object createObject(XRef *xref, const std::string &js); private: std::string js; @@ -515,6 +517,50 @@ class POPPLER_PRIVATE_EXPORT LinkResetForm : public LinkAction bool exclude; }; +//------------------------------------------------------------------------ +// LinkSubmitForm +//------------------------------------------------------------------------ + +class POPPLER_PRIVATE_EXPORT LinkSubmitForm : public LinkAction +{ +public: + enum SubmitFormFlag + { + NoOpFlag = 0, + ExcludeFlag = 1, + IncludeNoValueFieldsFlag = 1 << 1, + ExportFormatFlag = 1 << 2, + GetMethodFlag = 1 << 3, + SubmitCoordinatesFlag = 1 << 4, + XFDFFlag = 1 << 5, + IncludeAppendSavesFlag = 1 << 6, + IncludeAnnotationsFlag = 1 << 7, + SubmitPDFFlag = 1 << 8, + CanonicalFormatFlag = 1 << 9, + ExclNonUserAnnotsFlag = 1 << 10, + ExclFKeyFlag = 1 << 11, + // 13th high bit flag is undefined + EmbedFormFlag = 1 << 13, + }; + + // Build a LinkSubmitForm + explicit LinkSubmitForm(const Object *submitObj); + + ~LinkSubmitForm() override; + + bool isOk() const override { return !url.empty(); } + + LinkActionKind getKind() const override { return actionSubmitForm; } + const std::vector &getFields() const { return fields; }; + const std::string &getUrl() const { return url; }; + uint32_t getFlags() const { return flags; }; + +private: + std::vector fields; + std::string url; + uint32_t flags = 0; +}; + //------------------------------------------------------------------------ // LinkUnknown //------------------------------------------------------------------------ diff --git a/poppler/LocalPDFDocBuilder.cc b/poppler/LocalPDFDocBuilder.cc index bf4856e..72b277b 100644 --- a/poppler/LocalPDFDocBuilder.cc +++ b/poppler/LocalPDFDocBuilder.cc @@ -5,8 +5,9 @@ // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris -// Copyright 2010, 2022 Albert Astals Cid +// Copyright 2010, 2022, 2024 Albert Astals Cid // Copyright 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -18,14 +19,14 @@ // LocalPDFDocBuilder //------------------------------------------------------------------------ -std::unique_ptr LocalPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) +std::unique_ptr LocalPDFDocBuilder::buildPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword) { if (uri.cmpN("file://", 7) == 0) { - std::unique_ptr fileName(uri.copy()); + std::unique_ptr fileName = uri.copy(); fileName->del(0, 7); - return std::make_unique(std::move(fileName), ownerPassword, userPassword, guiDataA); + return std::make_unique(std::move(fileName), ownerPassword, userPassword); } else { - return std::make_unique(std::unique_ptr(uri.copy()), ownerPassword, userPassword, guiDataA); + return std::make_unique(uri.copy(), ownerPassword, userPassword); } } diff --git a/poppler/LocalPDFDocBuilder.h b/poppler/LocalPDFDocBuilder.h index 0595d1e..9d3c442 100644 --- a/poppler/LocalPDFDocBuilder.h +++ b/poppler/LocalPDFDocBuilder.h @@ -7,6 +7,7 @@ // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -25,7 +26,7 @@ class LocalPDFDocBuilder : public PDFDocBuilder { public: - std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) override; + std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}) override; bool supports(const GooString &uri) override; }; diff --git a/poppler/MarkedContentOutputDev.cc b/poppler/MarkedContentOutputDev.cc index e3a4cdc..56207e0 100644 --- a/poppler/MarkedContentOutputDev.cc +++ b/poppler/MarkedContentOutputDev.cc @@ -6,8 +6,9 @@ // // Copyright 2013 Igalia S.L. // Copyright 2018-2020, 2022 Albert Astals Cid -// Copyright 2021 Adrian Johnson +// Copyright 2021, 2023 Adrian Johnson // Copyright 2022 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -26,19 +27,16 @@ MarkedContentOutputDev::MarkedContentOutputDev(int mcidA, const Object &stmObj) currentColor.r = currentColor.g = currentColor.b = 0; } -MarkedContentOutputDev::~MarkedContentOutputDev() -{ - delete currentText; -} +MarkedContentOutputDev::~MarkedContentOutputDev() = default; void MarkedContentOutputDev::endSpan() { if (currentText && currentText->getLength()) { // The TextSpan takes ownership of currentText and // increases the reference count for currentFont. - textSpans.push_back(TextSpan(currentText, currentFont, currentColor)); + textSpans.push_back(TextSpan(std::move(currentText), currentFont, currentColor)); } - currentText = nullptr; + currentText.reset(); } void MarkedContentOutputDev::startPage(int pageNum, GfxState *state, XRef *xref) @@ -56,12 +54,12 @@ void MarkedContentOutputDev::endPage() pageWidth = pageHeight = 0.0; } -void MarkedContentOutputDev::beginForm(Ref id) +void MarkedContentOutputDev::beginForm(Object * /* obj */, Ref id) { formStack.push_back(id); } -void MarkedContentOutputDev::endForm(Ref id) +void MarkedContentOutputDev::endForm(Object * /* obj */, Ref id) { formStack.pop_back(); } @@ -200,7 +198,7 @@ void MarkedContentOutputDev::drawChar(GfxState *state, double xx, double yy, dou int n = unicodeMap->mapUnicode(u[i], buf, sizeof(buf)); if (n > 0) { if (currentText == nullptr) { - currentText = new GooString(); + currentText = std::make_unique(); } currentText->append(buf, n); } diff --git a/poppler/MarkedContentOutputDev.h b/poppler/MarkedContentOutputDev.h index dad39a7..07ec924 100644 --- a/poppler/MarkedContentOutputDev.h +++ b/poppler/MarkedContentOutputDev.h @@ -5,9 +5,10 @@ // This file is licensed under the GPLv2 or later // // Copyright 2013 Igalia S.L. -// Copyright 2018-2021 Albert Astals Cid -// Copyright 2021 Adrian Johnson +// Copyright 2018-2021, 2024 Albert Astals Cid +// Copyright 2021, 2023 Adrian Johnson // Copyright 2022 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -27,33 +28,21 @@ class UnicodeMap; class TextSpan { public: - TextSpan(const TextSpan &other) : data(other.data) { data->refcount++; } + TextSpan(const TextSpan &other) = default; - TextSpan &operator=(const TextSpan &other) - { - if (this != &other) { - data = other.data; - data->refcount++; - } - return *this; - } + TextSpan &operator=(const TextSpan &other) = default; - ~TextSpan() - { - if (data && --data->refcount == 0) { - delete data; - } - } + ~TextSpan() = default; const std::shared_ptr &getFont() const { return data->font; } - GooString *getText() const { return data->text; } + const GooString *getText() const { return data->text.get(); } GfxRGB &getColor() const { return data->color; } private: // Note: Takes ownership of strings, increases refcount for font. - TextSpan(GooString *text, std::shared_ptr font, const GfxRGB color) : data(new Data) + TextSpan(std::unique_ptr text, std::shared_ptr font, const GfxRGB color) : data(std::make_shared()) { - data->text = text; + data->text = std::move(text); data->font = std::move(font); data->color = color; } @@ -61,23 +50,18 @@ class TextSpan struct Data { std::shared_ptr font; - GooString *text; + std::unique_ptr text; GfxRGB color; - unsigned refcount; - Data() : refcount(1) { } + Data() = default; - ~Data() - { - assert(refcount == 0); - delete text; - } + ~Data() = default; Data(const Data &) = delete; Data &operator=(const Data &) = delete; }; - Data *data; + std::shared_ptr data; friend class MarkedContentOutputDev; }; @@ -100,8 +84,8 @@ class POPPLER_PRIVATE_EXPORT MarkedContentOutputDev : public OutputDev void startPage(int pageNum, GfxState *state, XRef *xref) override; void endPage() override; - void beginForm(Ref id) override; - void endForm(Ref id) override; + void beginForm(Object * /* obj */, Ref id) override; + void endForm(Object * /* obj */, Ref id) override; void drawChar(GfxState *state, double xx, double yy, double dx, double dy, double ox, double oy, CharCode c, int nBytes, const Unicode *u, int uLen) override; @@ -112,12 +96,12 @@ class POPPLER_PRIVATE_EXPORT MarkedContentOutputDev : public OutputDev private: void endSpan(); - bool inMarkedContent() const { return mcidStack.size() > 0; } + bool inMarkedContent() const { return !mcidStack.empty(); } bool contentStreamMatch(); bool needFontChange(const std::shared_ptr &font) const; std::shared_ptr currentFont; - GooString *currentText; + std::unique_ptr currentText; GfxRGB currentColor; TextSpanArray textSpans; int mcid; diff --git a/poppler/Movie.cc b/poppler/Movie.cc index 1c185a7..06e3d17 100644 --- a/poppler/Movie.cc +++ b/poppler/Movie.cc @@ -6,7 +6,7 @@ // Hugo Mercier (c) 2008 // Pino Toscano (c) 2008 // Carlos Garcia Campos (c) 2010 -// Albert Astals Cid (c) 2010, 2017-2019, 2022 +// Albert Astals Cid (c) 2010, 2017-2019, 2022, 2024 // Evgeny Stambulchik (c) 2019 // // This program is free software; you can redistribute it and/or modify @@ -45,8 +45,6 @@ MovieActivationParameters::MovieActivationParameters() zdenum = 1; } -MovieActivationParameters::~MovieActivationParameters() { } - void MovieActivationParameters::parseMovieActivation(const Object *aDict) { Object obj1 = aDict->dictLookup("Start"); @@ -172,7 +170,7 @@ void MovieActivationParameters::parseMovieActivation(const Object *aDict) void Movie::parseMovie(const Object *movieDict) { - fileName = nullptr; + fileName.reset(); rotationAngle = 0; width = -1; height = -1; @@ -225,10 +223,7 @@ void Movie::parseMovie(const Object *movieDict) } } -Movie::~Movie() -{ - delete fileName; -} +Movie::~Movie() = default; Movie::Movie(const Object *movieDict) { @@ -268,8 +263,6 @@ Movie::Movie(const Movie &other) if (other.fileName) { fileName = other.fileName->copy(); - } else { - fileName = nullptr; } } diff --git a/poppler/Movie.h b/poppler/Movie.h index 0c36510..b8405d3 100644 --- a/poppler/Movie.h +++ b/poppler/Movie.h @@ -5,7 +5,7 @@ //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Carlos Garcia Campos (c) 2010 -// Albert Astals Cid (c) 2017-2019, 2021, 2022 +// Albert Astals Cid (c) 2017-2019, 2021, 2022, 2024 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ struct MovieActivationParameters { MovieActivationParameters(); - ~MovieActivationParameters(); + ~MovieActivationParameters() = default; // parse from a "Movie Activation" dictionary void parseMovieActivation(const Object *aDict); @@ -86,7 +86,7 @@ class POPPLER_PRIVATE_EXPORT Movie bool isOk() const { return ok; } const MovieActivationParameters *getActivationParameters() const { return &MA; } - const GooString *getFileName() const { return fileName; } + const GooString *getFileName() const { return fileName.get(); } unsigned short getRotationAngle() const { return rotationAngle; } void getAspect(int *widthA, int *heightA) const @@ -115,7 +115,7 @@ class POPPLER_PRIVATE_EXPORT Movie Object poster; bool showPoster; - GooString *fileName; + std::unique_ptr fileName; MovieActivationParameters MA; }; diff --git a/poppler/SignatureHandler.cc b/poppler/NSSCryptoSignBackend.cc similarity index 63% rename from poppler/SignatureHandler.cc rename to poppler/NSSCryptoSignBackend.cc index e0579ca..521137d 100644 --- a/poppler/SignatureHandler.cc +++ b/poppler/NSSCryptoSignBackend.cc @@ -6,7 +6,7 @@ // // Copyright 2015, 2016 André Guerreiro // Copyright 2015 André Esser -// Copyright 2015, 2016, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright 2015, 2016, 2018, 2019, 2021-2023, 2025 Albert Astals Cid // Copyright 2015 Markus Kilås // Copyright 2017 Sebastian Rasmussen // Copyright 2017 Hans-Ulrich Jüttner @@ -17,16 +17,23 @@ // Copyright 2021 Theofilos Intzoglou // Copyright 2021 Marek Kasik // Copyright 2022 Erich E. Hoover +// Copyright 2023 Tobias Deiminger +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright 2023 Ingo Klöcker +// Copyright 2025 Juraj Šarinay // //======================================================================== #include -#include "SignatureHandler.h" -#include "goo/gdir.h" +#include "CryptoSignBackend.h" +#include "NSSCryptoSignBackend.h" #include "goo/gmem.h" +#include #include +#include +#include #include @@ -45,70 +52,6 @@ #include #include -// ASN.1 used in the (much simpler) time stamp request. From RFC3161 -// and other sources. - -/* -AlgorithmIdentifier ::= SEQUENCE { - algorithm OBJECT IDENTIFIER, - parameters ANY DEFINED BY algorithm OPTIONAL } - -- contains a value of the type - -- registered for use with the - -- algorithm object identifier value - -MessageImprint ::= SEQUENCE { - hashAlgorithm AlgorithmIdentifier, - hashedMessage OCTET STRING } -*/ - -struct MessageImprint -{ - SECAlgorithmID hashAlgorithm; - SECItem hashedMessage; -}; - -/* -Extension ::= SEQUENCE { - extnID OBJECT IDENTIFIER, - critical BOOLEAN DEFAULT FALSE, - extnValue OCTET STRING } -*/ - -struct Extension -{ - SECItem const extnID; - SECItem const critical; - SECItem const extnValue; -}; - -/* -Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension -*/ - -/* -TSAPolicyId ::= OBJECT IDENTIFIER - -TimeStampReq ::= SEQUENCE { - version INTEGER { v1(1) }, - messageImprint MessageImprint, - --a hash algorithm OID and the hash value of the data to be - --time-stamped - reqPolicy TSAPolicyId OPTIONAL, - nonce INTEGER OPTIONAL, - certReq BOOLEAN DEFAULT FALSE, - extensions [0] IMPLICIT Extensions OPTIONAL } -*/ - -struct TimeStampReq -{ - SECItem version; - MessageImprint messageImprint; - SECItem reqPolicy; - SECItem nonce; - SECItem certReq; - Extension *extensions; -}; - /** * General name, defined by RFC 3280. */ @@ -257,6 +200,10 @@ const SEC_ASN1Template TimeStampReq_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullp { 0, 0, nullptr, 0 } }; */ +static NSSCMSMessage *CMS_MessageCreate(SECItem *cms_item); +static NSSCMSSignedData *CMS_SignedDataCreate(NSSCMSMessage *cms_msg); +static NSSCMSSignerInfo *CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data); + // a dummy, actually static char *passwordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg) { @@ -270,106 +217,29 @@ static void shutdownNss() } } -// SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are -// not exported from libsmime, so copy them here. Sigh. - -static SECStatus my_SEC_StringToOID(SECItem *to, const char *from, PRUint32 len) -{ - PRUint32 decimal_numbers = 0; - PRUint32 result_bytes = 0; - SECStatus rv; - PRUint8 result[1024]; - - static const PRUint32 max_decimal = 0xffffffff / 10; - static const char OIDstring[] = { "OID." }; - - if (!from || !to) { - PORT_SetError(SEC_ERROR_INVALID_ARGS); - return SECFailure; - } - if (!len) { - len = PL_strlen(from); - } - if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) { - from += 4; /* skip leading "OID." if present */ - len -= 4; - } - if (!len) { - bad_data: - PORT_SetError(SEC_ERROR_BAD_DATA); - return SECFailure; - } - do { - PRUint32 decimal = 0; - while (len > 0 && (*from >= '0' && *from <= '9')) { - PRUint32 addend = *from++ - '0'; - --len; - if (decimal > max_decimal) { /* overflow */ - goto bad_data; - } - decimal = (decimal * 10) + addend; - if (decimal < addend) { /* overflow */ - goto bad_data; - } - } - if (len != 0 && *from != '.') { - goto bad_data; - } - if (decimal_numbers == 0) { - if (decimal > 2) { - goto bad_data; - } - result[0] = decimal * 40; - result_bytes = 1; - } else if (decimal_numbers == 1) { - if (decimal > 40) { - goto bad_data; - } - result[0] += decimal; - } else { - /* encode the decimal number, */ - PRUint8 *rp; - PRUint32 num_bytes = 0; - PRUint32 tmp = decimal; - while (tmp) { - num_bytes++; - tmp >>= 7; - } - if (!num_bytes) { - ++num_bytes; /* use one byte for a zero value */ - } - if (num_bytes + result_bytes > sizeof result) { - goto bad_data; - } - tmp = num_bytes; - rp = result + result_bytes - 1; - rp[tmp] = static_cast(decimal & 0x7f); - decimal >>= 7; - while (--tmp > 0) { - rp[tmp] = static_cast(decimal | 0x80); - decimal >>= 7; - } - result_bytes += num_bytes; - } - ++decimal_numbers; - if (len > 0) { /* skip trailing '.' */ - ++from; - --len; - } - } while (len > 0); - /* now result contains result_bytes of data */ - if (to->data && to->len >= result_bytes) { - to->len = result_bytes; - PORT_Memcpy(to->data, result, to->len); - rv = SECSuccess; - } else { - SECItem result_item = { siBuffer, nullptr, 0 }; - result_item.data = result; - result_item.len = result_bytes; - rv = SECITEM_CopyItem(nullptr, to, &result_item); - } - return rv; -} +// An ASN.1 object identifier (OID) is typically written as a dot-separated sequence of integers +// and encoded as a sequence of bytes. Because we only ever need to handle BER encoded OIDs, keep +// them encoded from the beginning to avoid conversions at run time. +// +// The mapping from the sequence of integers to an array of bytes follows ITU-T X.690 clause 8.19. +// The first two components are encoded in a single output byte: out[0] = 40 * in[0] + in[1] +// +// EXAMPLE: 1.2 -> 40 * 1 + 2 = 0x2a +// +// From the third component onwards: +// 1. interpret the integer in base 128 +// 2. map the 7-bit digits to bytes +// 3. set the most significant bit in all but the least signigicant byte to 1 +// 4. output the bytes from left to right +// +// EXAMPLE: 840 = 110 1001000 -> 10000110 01001000 = 0x86 0x48 +// 113549 = 110 1110111 0001101 -> 10000110 11110111 00001101 = 0x86 0xf7 0x0d +// +// As a consequence, a component that fits within 7 bits can be output unchanged as a single byte. +// EXAMPLE: .1.9.16.2.47 -> ... 0x01 0x09 0x10 0x02 0x2f +// +// 1.2.840.113549.1.9.16.2.47 +constexpr unsigned char OID_SIGNINGCERTIFICATEV2[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x2f }; static NSSCMSAttribute *my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only) { @@ -429,7 +299,7 @@ static SECStatus my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *ob while (*p++) { n++; } - dest = static_cast(PORT_ArenaGrow(poolp, *array, (n + 1) * sizeof(void *), (n + 2) * sizeof(void *))); + dest = static_cast(PORT_ArenaGrow(poolp, static_cast(*array), (n + 1) * sizeof(void *), (n + 2) * sizeof(void *))); } if (dest == nullptr) { @@ -490,16 +360,70 @@ static SECStatus my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); } -unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId) +static SECOidTag ConvertHashAlgorithmToNss(HashAlgorithm digestAlgId) +{ + switch (digestAlgId) { + case HashAlgorithm::Md2: + return SEC_OID_MD2; + case HashAlgorithm::Md5: + return SEC_OID_MD5; + case HashAlgorithm::Sha1: + return SEC_OID_SHA1; + case HashAlgorithm::Sha256: + return SEC_OID_SHA256; + case HashAlgorithm::Sha384: + return SEC_OID_SHA384; + case HashAlgorithm::Sha512: + return SEC_OID_SHA512; + case HashAlgorithm::Sha224: + return SEC_OID_SHA224; + case HashAlgorithm::Unknown: + return SEC_OID_UNKNOWN; + } + return SEC_OID_UNKNOWN; +} + +static HashAlgorithm ConvertHashTypeFromNss(HASH_HashType type) +{ + switch (type) { + case HASH_AlgMD2: + return HashAlgorithm::Md2; + case HASH_AlgMD5: + return HashAlgorithm::Md5; + case HASH_AlgSHA1: + return HashAlgorithm::Sha1; + case HASH_AlgSHA256: + return HashAlgorithm::Sha256; + case HASH_AlgSHA384: + return HashAlgorithm::Sha384; + case HASH_AlgSHA512: + return HashAlgorithm::Sha512; + case HASH_AlgSHA224: + return HashAlgorithm::Sha224; +#if NSS_VMAJOR >= 3 && NSS_VMINOR >= 91 + // TODO Expose this in HashAlgorithm if PDF supports them + case HASH_AlgSHA3_224: + case HASH_AlgSHA3_256: + case HASH_AlgSHA3_384: + case HASH_AlgSHA3_512: +#endif + case HASH_AlgNULL: + case HASH_AlgTOTAL: + return HashAlgorithm::Unknown; + } + return HashAlgorithm::Unknown; +} + +static unsigned int digestLength(HashAlgorithm digestAlgId) { switch (digestAlgId) { - case SEC_OID_SHA1: + case HashAlgorithm::Sha1: return 20; - case SEC_OID_SHA256: + case HashAlgorithm::Sha256: return 32; - case SEC_OID_SHA384: + case HashAlgorithm::Sha384: return 48; - case SEC_OID_SHA512: + case HashAlgorithm::Sha512: return 64; default: printf("ERROR: Unrecognized Hash ID\n"); @@ -507,42 +431,21 @@ unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId) } } -SECOidTag SignatureHandler::getHashOidTag(const char *digestName) -{ - SECOidTag tag = SEC_OID_UNKNOWN; - if (strcmp(digestName, "SHA1") == 0) { - tag = SEC_OID_SHA1; - } else if (strcmp(digestName, "SHA256") == 0) { - tag = SEC_OID_SHA256; - } else if (strcmp(digestName, "SHA384") == 0) { - tag = SEC_OID_SHA384; - } else if (strcmp(digestName, "SHA512") == 0) { - tag = SEC_OID_SHA512; - } - return tag; -} - -std::string SignatureHandler::getSignerName() +std::string NSSSignatureVerification::getSignerName() const { - char *commonName; - if (!NSS_IsInitialized()) { return {}; } - - if (!signing_cert && !CMSSignerInfo) { + if (!CMSSignerInfo) { return {}; } - if (!signing_cert) { - signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); - } - + auto signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); if (!signing_cert) { return {}; } - commonName = CERT_GetCommonName(&signing_cert->subject); + char *commonName = CERT_GetCommonName(&signing_cert->subject); if (!commonName) { return {}; } @@ -552,40 +455,30 @@ std::string SignatureHandler::getSignerName() return name; } -const char *SignatureHandler::getSignerSubjectDN() +std::string NSSSignatureVerification::getSignerSubjectDN() const { - if (!signing_cert && !CMSSignerInfo) { - return nullptr; - } - - if (!signing_cert) { - signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); + if (!CMSSignerInfo) { + return {}; } - + auto signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); if (!signing_cert) { - return nullptr; + return {}; } - - return signing_cert->subjectName; + return std::string { signing_cert->subjectName }; } -HASH_HashType SignatureHandler::getHashAlgorithm() +std::chrono::system_clock::time_point NSSSignatureVerification::getSigningTime() const { - if (hash_context && hash_context->hashobj) { - return hash_context->hashobj->type; + if (!CMSSignerInfo) { + return {}; } - return HASH_AlgNULL; -} - -time_t SignatureHandler::getSigningTime() -{ PRTime sTime; // time in microseconds since the epoch if (NSS_CMSSignerInfo_GetSigningTime(CMSSignerInfo, &sTime) != SECSuccess) { - return 0; + return {}; } - return static_cast(sTime / 1000000); + return std::chrono::system_clock::from_time_t(static_cast(sTime / 1000000)); } static X509CertificateInfo::EntityInfo getEntityInfo(CERTName *entityName) @@ -656,51 +549,57 @@ static std::unique_ptr getCertificateInfoFromCERT(CERTCerti // public key info X509CertificateInfo::PublicKeyInfo pkInfo; SECKEYPublicKey *pk = CERT_ExtractPublicKey(cert); - switch (pk->keyType) { - case rsaKey: - pkInfo.publicKey = SECItemToGooString(pk->u.rsa.modulus); - pkInfo.publicKeyType = RSAKEY; - break; - case dsaKey: - pkInfo.publicKey = SECItemToGooString(pk->u.dsa.publicValue); - pkInfo.publicKeyType = DSAKEY; - break; - case ecKey: - pkInfo.publicKey = SECItemToGooString(pk->u.ec.publicValue); - pkInfo.publicKeyType = ECKEY; - break; - default: - pkInfo.publicKey = SECItemToGooString(cert->subjectPublicKeyInfo.subjectPublicKey); + if (pk) { + switch (pk->keyType) { + case rsaKey: + pkInfo.publicKey = SECItemToGooString(pk->u.rsa.modulus); + pkInfo.publicKeyType = RSAKEY; + break; + case dsaKey: + pkInfo.publicKey = SECItemToGooString(pk->u.dsa.publicValue); + pkInfo.publicKeyType = DSAKEY; + break; + case ecKey: + pkInfo.publicKey = SECItemToGooString(pk->u.ec.publicValue); + pkInfo.publicKeyType = ECKEY; + break; + default: + pkInfo.publicKey = SECItemToGooString(cert->subjectPublicKeyInfo.subjectPublicKey); + pkInfo.publicKeyType = OTHERKEY; + break; + } + pkInfo.publicKeyStrength = SECKEY_PublicKeyStrengthInBits(pk); + SECKEY_DestroyPublicKey(pk); + } else { pkInfo.publicKeyType = OTHERKEY; - break; } - pkInfo.publicKeyStrength = SECKEY_PublicKeyStrengthInBits(pk); certInfo->setPublicKeyInfo(std::move(pkInfo)); certInfo->setKeyUsageExtensions(cert->keyUsage); certInfo->setCertificateDER(SECItemToGooString(cert->derCert)); certInfo->setIsSelfSigned(CERT_CompareName(&cert->subject, &cert->issuer) == SECEqual); - SECKEY_DestroyPublicKey(pk); - return certInfo; } -std::unique_ptr SignatureHandler::getCertificateInfo() const +std::unique_ptr NSSSignatureVerification::getCertificateInfo() const { - if (CMSSignerInfo) { - CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); - if (!cert) { - return nullptr; - } - return getCertificateInfoFromCERT(cert); - } else { - if (!signing_cert) { - return nullptr; - } + if (!CMSSignerInfo) { + return nullptr; + } + CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB()); + if (!cert) { + return nullptr; + } + return getCertificateInfoFromCERT(cert); +} - return getCertificateInfoFromCERT(signing_cert); +std::unique_ptr NSSSignatureCreation::getCertificateInfo() const +{ + if (!signing_cert) { + return nullptr; } + return getCertificateInfoFromCERT(signing_cert); } static std::optional getDefaultFirefoxCertDB() @@ -719,22 +618,21 @@ static std::optional getDefaultFirefoxCertDB() const std::string firefoxPath = std::string(env) + "/.mozilla/firefox/"; #endif - GDir firefoxDir(firefoxPath.c_str()); - std::unique_ptr entry; - while (entry = firefoxDir.getNextEntry(), entry != nullptr) { - if (entry->isDir() && entry->getName()->toStr().find("default") != std::string::npos) { - return entry->getFullPath()->toStr(); + std::error_code ec; // ensures directory_iterator doesn't throw exceptions + for (const auto &entry : std::filesystem::directory_iterator { firefoxPath, ec }) { + if (entry.is_directory() && entry.path().string().find("default") != std::string::npos) { + return entry.path().string(); } } return {}; } -std::string SignatureHandler::sNssDir; +std::string NSSSignatureConfiguration::sNssDir; /** * Initialise NSS */ -void SignatureHandler::setNSSDir(const GooString &nssDir) +void NSSSignatureConfiguration::setNSSDir(const GooString &nssDir) { static bool setNssDirCalled = false; @@ -769,98 +667,107 @@ void SignatureHandler::setNSSDir(const GooString &nssDir) homeNssDb.append("/.pki/nssdb"); initSuccess = (NSS_Init(homeNssDb.c_str()) == SECSuccess); sNssDir = homeNssDb.toStr(); - if (!initSuccess) { - NSS_NoDB_Init(nullptr); - } } } if (initSuccess) { // Make sure NSS root certificates module is loaded SECMOD_AddNewModule("Root Certs", "libnssckbi.so", 0, 0); + } else { + fprintf(stderr, "NSS_Init failed: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT)); + NSS_NoDB_Init(nullptr); } } -std::string SignatureHandler::getNSSDir() +std::string NSSSignatureConfiguration::getNSSDir() { return sNssDir; } static std::function PasswordFunction; -void SignatureHandler::setNSSPasswordCallback(const std::function &f) +void NSSSignatureConfiguration::setNSSPasswordCallback(const std::function &f) { PasswordFunction = f; } -SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length) : hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr) +NSSSignatureVerification::NSSSignatureVerification(std::vector &&p7data) : p7(std::move(p7data)), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr) { - setNSSDir({}); - CMSitem.data = p7; - CMSitem.len = p7_length; + NSSSignatureConfiguration::setNSSDir({}); + CMSitem.data = p7.data(); + CMSitem.len = p7.size(); CMSMessage = CMS_MessageCreate(&CMSitem); CMSSignedData = CMS_SignedDataCreate(CMSMessage); if (CMSSignedData) { CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData); - hash_context = initHashContext(); + SECAlgorithmID **algs = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData); + while (*algs != nullptr) { + SECItem usedAlgorithm = (*algs)->algorithm; + auto hashAlgorithm = SECOID_FindOIDTag(&usedAlgorithm); + HASH_HashType hashType = HASH_GetHashTypeByOidTag(hashAlgorithm); + hashContext = HashContext::create(ConvertHashTypeFromNss(hashType)); + + if (hashContext) { + break; + } + ++algs; + } } } -SignatureHandler::SignatureHandler(const char *certNickname, SECOidTag digestAlgTag) - : hash_length(digestLength(digestAlgTag)), digest_alg_tag(digestAlgTag), CMSitem(), hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr) +NSSSignatureCreation::NSSSignatureCreation(const std::string &certNickname, HashAlgorithm digestAlgTag) : hashContext(HashContext::create(digestAlgTag)), signing_cert(nullptr) { - setNSSDir({}); - CMSMessage = NSS_CMSMessage_Create(nullptr); - signing_cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname); - hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digestAlgTag)); + NSSSignatureConfiguration::setNSSDir({}); + signing_cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname.c_str()); } -SignatureHandler::SignatureHandler() : hash_length(), digest_alg_tag(), CMSitem(), hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr) +HashAlgorithm NSSSignatureVerification::getHashAlgorithm() const { - setNSSDir({}); - CMSMessage = NSS_CMSMessage_Create(nullptr); + if (hashContext) { + return hashContext->getHashAlgorithm(); + } else { + return HashAlgorithm::Unknown; + } } -HASHContext *SignatureHandler::initHashContext() +void NSSSignatureVerification::addData(unsigned char *data_block, int data_len) { - - SECItem usedAlgorithm = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData)[0]->algorithm; - hash_length = digestLength(SECOID_FindOIDTag(&usedAlgorithm)); - HASH_HashType hashType; - hashType = HASH_GetHashTypeByOidTag(SECOID_FindOIDTag(&usedAlgorithm)); - return HASH_Create(hashType); + if (hashContext) { + hashContext->updateHash(data_block, data_len); + } } -void SignatureHandler::updateHash(unsigned char *data_block, int data_len) +void NSSSignatureCreation::addData(unsigned char *data_block, int data_len) { - if (hash_context) { - HASH_Update(hash_context, data_block, data_len); - } + hashContext->updateHash(data_block, data_len); } -void SignatureHandler::restartHash() +NSSSignatureCreation::~NSSSignatureCreation() { - if (hash_context) { - HASH_Destroy(hash_context); + if (signing_cert) { + CERT_DestroyCertificate(signing_cert); } - hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digest_alg_tag)); } - -SignatureHandler::~SignatureHandler() +NSSSignatureVerification::~NSSSignatureVerification() { - SECITEM_FreeItem(&CMSitem, PR_FALSE); if (CMSMessage) { + // in the CMS_SignedDataCreate, we malloc some memory + // inside the CMSSignedData structure + // which is otherwise destructed by NSS_CMSMessage_Destroy + // but given we did the malloc ourselves + // we also need to free it ourselves. + // After we free the surrounding memory but we need + // a handle to it before. + CERTCertificate **toFree = nullptr; + if (CMSSignedData) { + toFree = CMSSignedData->tempCerts; + } NSS_CMSMessage_Destroy(CMSMessage); + free(static_cast(toFree)); } - - if (hash_context) { - HASH_Destroy(hash_context); - } - - free(temp_certs); } -NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem *cms_item) +static NSSCMSMessage *CMS_MessageCreate(SECItem *cms_item) { if (cms_item->data) { return NSS_CMSMessage_CreateFromDER(cms_item, nullptr, nullptr /* Content callback */ @@ -873,7 +780,7 @@ NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem *cms_item) } } -NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage *cms_msg) +static NSSCMSSignedData *CMS_SignedDataCreate(NSSCMSMessage *cms_msg) { if (!NSS_CMSMessage_IsSigned(cms_msg)) { error(errInternal, 0, "Input couldn't be parsed as a CMS signature"); @@ -898,20 +805,18 @@ NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage *cms_msg) // tempCerts field needs to be filled for complete memory release by NSSCMSSignedData_Destroy signedData->tempCerts = (CERTCertificate **)gmallocn(i + 1, sizeof(CERTCertificate *)); - memset(signedData->tempCerts, 0, (i + 1) * sizeof(CERTCertificate *)); + memset(static_cast(signedData->tempCerts), 0, (i + 1) * sizeof(CERTCertificate *)); // store the addresses of these temporary certificates for future release for (i = 0; signedData->rawCerts[i]; ++i) { signedData->tempCerts[i] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], nullptr, 0, 0); } - - temp_certs = signedData->tempCerts; return signedData; } else { return nullptr; } } -NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data) +static NSSCMSSignerInfo *CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data) { NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0); if (!signerInfo) { @@ -942,10 +847,8 @@ static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_c } } -SignatureValidationStatus SignatureHandler::validateSignature() +SignatureValidationStatus NSSSignatureVerification::validateSignature() { - unsigned char *digest_buffer = nullptr; - if (!CMSSignedData) { return SIGNATURE_GENERIC_ERROR; } @@ -954,18 +857,15 @@ SignatureValidationStatus SignatureHandler::validateSignature() return SIGNATURE_GENERIC_ERROR; } - if (!hash_context) { + if (!hashContext) { return SIGNATURE_GENERIC_ERROR; } - digest_buffer = (unsigned char *)PORT_Alloc(hash_length); - unsigned int result_len = 0; - - HASH_End(hash_context, digest_buffer, &result_len, hash_length); + std::vector digest_buffer = hashContext->endHash(); SECItem digest; - digest.data = digest_buffer; - digest.len = hash_length; + digest.data = digest_buffer.data(); + digest.len = digest_buffer.size(); if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr) { CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound; @@ -977,30 +877,32 @@ SignatureValidationStatus SignatureHandler::validateSignature() This means it's not a detached type signature so the digest is contained in SignedData->contentInfo */ - if (memcmp(digest.data, content_info_data->data, hash_length) == 0 && digest.len == content_info_data->len) { - PORT_Free(digest_buffer); + if (digest.len == content_info_data->len && memcmp(digest.data, content_info_data->data, digest.len) == 0) { return SIGNATURE_VALID; } else { - PORT_Free(digest_buffer); return SIGNATURE_DIGEST_MISMATCH; } } else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess) { - - PORT_Free(digest_buffer); return NSS_SigTranslate(CMSSignerInfo->verificationStatus); } else { - PORT_Free(digest_buffer); return SIGNATURE_VALID; } } -CertificateValidationStatus SignatureHandler::validateCertificate(time_t validation_time, bool ocspRevocationCheck, bool useAIACertFetch) +void NSSSignatureVerification::validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function &doneCallback) { + cachedValidationStatus.reset(); CERTCertificate *cert; if (!CMSSignerInfo) { - return CERTIFICATE_GENERIC_ERROR; + validationStatus = std::async([doneCallback]() { + if (doneCallback) { + doneCallback(); + } + return CERTIFICATE_GENERIC_ERROR; + }); + return; } if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr) { @@ -1008,8 +910,8 @@ CertificateValidationStatus SignatureHandler::validateCertificate(time_t validat } PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now - if (validation_time > 0) { - vTime = 1000000 * (PRTime)validation_time; + if (validation_time > std::chrono::system_clock::time_point {}) { + vTime = 1000000 * (PRTime)std::chrono::system_clock::to_time_t(validation_time); } CERTValInParam inParams[4]; inParams[0].type = cert_pi_revocationFlags; @@ -1030,102 +932,133 @@ CertificateValidationStatus SignatureHandler::validateCertificate(time_t validat CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, nullptr, CMSSignerInfo->cmsg->pwfn_arg); - switch (PORT_GetError()) { - // 0 not defined in SECErrorCodes, it means success for this purpose. - case 0: - return CERTIFICATE_TRUSTED; + // Here we are just faking the asynchronousness. It should + // somehow be the call to CERT_PXIXVerifyCert that would + // be put in the thread, but I'm not sure about all of the + // thread safety of nss. + + validationStatus = std::async([result = PORT_GetError(), doneCallback]() { + if (doneCallback) { + doneCallback(); + } - case SEC_ERROR_UNKNOWN_ISSUER: - return CERTIFICATE_UNKNOWN_ISSUER; + switch (result) { + // 0 not defined in SECErrorCodes, it means success for this purpose. + case 0: + return CERTIFICATE_TRUSTED; - case SEC_ERROR_UNTRUSTED_ISSUER: - return CERTIFICATE_UNTRUSTED_ISSUER; + case SEC_ERROR_UNKNOWN_ISSUER: + return CERTIFICATE_UNKNOWN_ISSUER; - case SEC_ERROR_REVOKED_CERTIFICATE: - return CERTIFICATE_REVOKED; + case SEC_ERROR_UNTRUSTED_ISSUER: + return CERTIFICATE_UNTRUSTED_ISSUER; - case SEC_ERROR_EXPIRED_CERTIFICATE: - return CERTIFICATE_EXPIRED; - } + case SEC_ERROR_REVOKED_CERTIFICATE: + return CERTIFICATE_REVOKED; + + case SEC_ERROR_EXPIRED_CERTIFICATE: + return CERTIFICATE_EXPIRED; + } - return CERTIFICATE_GENERIC_ERROR; + return CERTIFICATE_GENERIC_ERROR; + }); } -std::unique_ptr SignatureHandler::signDetached(const char *password) const +CertificateValidationStatus NSSSignatureVerification::validateCertificateResult() { - if (!hash_context) { - return nullptr; + if (cachedValidationStatus) { + return cachedValidationStatus.value(); + } + if (!validationStatus.valid()) { + return CERTIFICATE_NOT_VERIFIED; + } + validationStatus.wait(); + cachedValidationStatus = validationStatus.get(); + return cachedValidationStatus.value(); +} + +std::variant, CryptoSign::SigningError> NSSSignatureCreation::signDetached(const std::string &password) +{ + if (!hashContext) { + return CryptoSign::SigningError::InternalError; } - unsigned char *digest_buffer = reinterpret_cast(PORT_Alloc(hash_length)); - unsigned int result_len = 0; - HASH_End(hash_context, digest_buffer, &result_len, hash_length); + std::vector digest_buffer = hashContext->endHash(); SECItem digest; - digest.data = digest_buffer; - digest.len = result_len; + digest.data = digest_buffer.data(); + digest.len = digest_buffer.size(); ///////////////////////////////////// /// Code from LibreOffice under MPLv2 ///////////////////////////////////// - - NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(nullptr); + struct NSSCMSMessageDestroyer + { + void operator()(NSSCMSMessage *message) { NSS_CMSMessage_Destroy(message); } + }; + std::unique_ptr cms_msg { NSS_CMSMessage_Create(nullptr) }; if (!cms_msg) { - return nullptr; + return CryptoSign::SigningError::GenericError; } - NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg); + NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg.get()); if (!cms_sd) { - return nullptr; + return CryptoSign::SigningError::GenericError; } - NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg); + NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg.get()); - if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg, cms_cinfo, cms_sd) != SECSuccess) { - return nullptr; + if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg.get(), cms_cinfo, cms_sd) != SECSuccess) { + return CryptoSign::SigningError::GenericError; } cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd); // Attach NULL data as detached data - if (NSS_CMSContentInfo_SetContent_Data(cms_msg, cms_cinfo, nullptr, PR_TRUE) != SECSuccess) { - return nullptr; + if (NSS_CMSContentInfo_SetContent_Data(cms_msg.get(), cms_cinfo, nullptr, PR_TRUE) != SECSuccess) { + return CryptoSign::SigningError::GenericError; } // hardcode SHA256 these days... - NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg, signing_cert, SEC_OID_SHA256); + NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg.get(), signing_cert, SEC_OID_SHA256); if (!cms_signer) { - return nullptr; + return CryptoSign::SigningError::GenericError; } if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { - return nullptr; + return CryptoSign::SigningError::GenericError; } if (NSS_CMSSignedData_AddCertificate(cms_sd, signing_cert) != SECSuccess) { - return nullptr; + return CryptoSign::SigningError::GenericError; } if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess) { - return nullptr; + return CryptoSign::SigningError::GenericError; } if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA256, &digest) != SECSuccess) { - return nullptr; + return CryptoSign::SigningError::GenericError; } + struct PLArenaFreeFalse + { + void operator()(PLArenaPool *arena) { PORT_FreeArena(arena, PR_FALSE); } + }; + std::unique_ptr arena { PORT_NewArena(CryptoSign::maxSupportedSignatureSize) }; + // Add the signing certificate as a signed attribute. ESSCertIDv2 *aCertIDs[2]; ESSCertIDv2 aCertID; // Write ESSCertIDv2.hashAlgorithm. aCertID.hashAlgorithm.algorithm.data = nullptr; aCertID.hashAlgorithm.parameters.data = nullptr; - SECOID_SetAlgorithmID(nullptr, &aCertID.hashAlgorithm, SEC_OID_SHA256, nullptr); + SECOID_SetAlgorithmID(arena.get(), &aCertID.hashAlgorithm, SEC_OID_SHA256, nullptr); // Write ESSCertIDv2.certHash. SECItem aCertHashItem; unsigned char certhash[32]; SECStatus rv = PK11_HashBuf(SEC_OID_SHA256, certhash, signing_cert->derCert.data, signing_cert->derCert.len); if (rv != SECSuccess) { - return nullptr; + return {}; } aCertHashItem.type = siBuffer; @@ -1148,7 +1081,7 @@ std::unique_ptr SignatureHandler::signDetached(const char *password) SECItem *pEncodedCertificate = SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate, SigningCertificateV2Template); if (!pEncodedCertificate) { - return nullptr; + return {}; } NSSCMSAttribute aAttribute; @@ -1163,15 +1096,15 @@ std::unique_ptr SignatureHandler::signDetached(const char *password) aAttribute.values = pAttributeValues; SECOidData aOidData; - aOidData.oid.data = nullptr; /* * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::= * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) * smime(16) id-aa(2) 47 } */ - if (my_SEC_StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess) { - return nullptr; - } + auto cert_oid_buffer = std::to_array(OID_SIGNINGCERTIFICATEV2); + aOidData.oid.type = siBuffer; + aOidData.oid.data = cert_oid_buffer.data(); + aOidData.oid.len = cert_oid_buffer.size(); aOidData.offset = SEC_OID_UNKNOWN; aOidData.desc = "id-aa-signingCertificateV2"; @@ -1182,32 +1115,27 @@ std::unique_ptr SignatureHandler::signDetached(const char *password) aAttribute.encoded = PR_TRUE; if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer, &aAttribute) != SECSuccess) { - return nullptr; + return CryptoSign::SigningError::GenericError; } SECItem cms_output; cms_output.data = nullptr; cms_output.len = 0; - PLArenaPool *arena = PORT_NewArena(10000); - NSSCMSEncoderContext *cms_ecx = NSS_CMSEncoder_Start(cms_msg, nullptr, nullptr, &cms_output, arena, passwordCallback, const_cast(password), nullptr, nullptr, nullptr, nullptr); + NSSCMSEncoderContext *cms_ecx = NSS_CMSEncoder_Start(cms_msg.get(), nullptr, nullptr, &cms_output, arena.get(), passwordCallback, password.empty() ? nullptr : const_cast(password.c_str()), nullptr, nullptr, nullptr, nullptr); if (!cms_ecx) { - PORT_FreeArena(arena, PR_FALSE); - return nullptr; + return CryptoSign::SigningError::GenericError; } if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess) { - PORT_FreeArena(arena, PR_FALSE); - return nullptr; + return CryptoSign::SigningError::GenericError; } - GooString *signature = new GooString(reinterpret_cast(cms_output.data), cms_output.len); + auto signature = std::vector(cms_output.data, cms_output.data + cms_output.len); SECITEM_FreeItem(pEncodedCertificate, PR_TRUE); - NSS_CMSMessage_Destroy(cms_msg); - PORT_FreeArena(arena, PR_FALSE); - return std::unique_ptr(signature); + return signature; } static char *GetPasswordFunction(PK11SlotInfo *slot, PRBool /*retry*/, void * /*arg*/) @@ -1219,11 +1147,31 @@ static char *GetPasswordFunction(PK11SlotInfo *slot, PRBool /*retry*/, void * /* return nullptr; } -std::vector> SignatureHandler::getAvailableSigningCertificates() +std::unique_ptr NSSCryptoSignBackend::createVerificationHandler(std::vector &&pkcs7, CryptoSign::SignatureType type) +{ + switch (type) { + case CryptoSign::SignatureType::unknown_signature_type: + case CryptoSign::SignatureType::unsigned_signature_field: + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + return {}; + case CryptoSign::SignatureType::ETSI_CAdES_detached: + case CryptoSign::SignatureType::adbe_pkcs7_detached: + case CryptoSign::SignatureType::adbe_pkcs7_sha1: + return std::make_unique(std::move(pkcs7)); + } + return {}; +} + +std::unique_ptr NSSCryptoSignBackend::createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) +{ + return std::make_unique(certID, digestAlgTag); +} + +std::vector> NSSCryptoSignBackend::getAvailableSigningCertificates() { // set callback, in case one of the slots has a password set PK11_SetPasswordFunc(GetPasswordFunction); - setNSSDir({}); + NSSSignatureConfiguration::setNSSDir({}); std::vector> certsList; PK11SlotList *slotList = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr); @@ -1261,3 +1209,37 @@ std::vector> SignatureHandler::getAvailable return certsList; } + +void HashContext::updateHash(unsigned char *data_block, int data_len) +{ + HASH_Update(hash_context.get(), data_block, data_len); +} + +std::vector HashContext::endHash() +{ + auto hash_length = digestLength(digest_alg_tag); + std::vector digestBuffer(hash_length); + unsigned int result_length = 0; + HASH_End(hash_context.get(), digestBuffer.data(), &result_length, digestBuffer.size()); + digestBuffer.resize(result_length); + + return digestBuffer; +} + +HashContext::HashContext(HashAlgorithm algorithm, private_tag) : hash_context { HASH_Create(HASH_GetHashTypeByOidTag(ConvertHashAlgorithmToNss(algorithm))) }, digest_alg_tag(algorithm) { } + +std::unique_ptr HashContext::create(HashAlgorithm algorithm) +{ + auto ctx = std::make_unique(algorithm, private_tag {}); + if (ctx->hash_context) { + return ctx; + } + return {}; +} + +HashAlgorithm HashContext::getHashAlgorithm() const +{ + return digest_alg_tag; +} + +NSSCryptoSignBackend::~NSSCryptoSignBackend() = default; diff --git a/poppler/NSSCryptoSignBackend.h b/poppler/NSSCryptoSignBackend.h new file mode 100644 index 0000000..606d859 --- /dev/null +++ b/poppler/NSSCryptoSignBackend.h @@ -0,0 +1,150 @@ +//======================================================================== +// +// SignatureHandler.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2015 André Guerreiro +// Copyright 2015 André Esser +// Copyright 2015, 2017, 2019, 2021 Albert Astals Cid +// Copyright 2017 Hans-Ulrich Jüttner +// Copyright 2018 Chinmoy Ranjan Pradhan +// Copyright 2018 Oliver Sander +// Copyright 2020 Thorsten Behrens +// Copyright 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden +// Copyright 2021 Theofilos Intzoglou +// Copyright 2021 Marek Kasik +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela +// +//======================================================================== + +#ifndef SIGNATURE_HANDLER_H +#define SIGNATURE_HANDLER_H + +#include "goo/GooString.h" +#include "SignatureInfo.h" +#include "CertificateInfo.h" +#include "poppler_private_export.h" +#include "HashAlgorithm.h" + +#include +#include +#include +#include + +/* NSPR Headers */ +#include + +/* NSS headers */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "CryptoSignBackend.h" + +class HashContext +{ + class private_tag + { + }; + +public: + HashContext(HashAlgorithm algorithm, private_tag); + void updateHash(unsigned char *data_block, int data_len); + std::vector endHash(); + HashAlgorithm getHashAlgorithm() const; + ~HashContext() = default; + static std::unique_ptr create(HashAlgorithm algorithm); + +private: + struct HashDestroyer + { + void operator()(HASHContext *hash) { HASH_Destroy(hash); } + }; + std::unique_ptr hash_context; + HashAlgorithm digest_alg_tag; +}; + +class NSSSignatureVerification final : public CryptoSign::VerificationInterface +{ +public: + explicit NSSSignatureVerification(std::vector &&p7data); + ~NSSSignatureVerification() final; + SignatureValidationStatus validateSignature() final; + std::chrono::system_clock::time_point getSigningTime() const final; + std::string getSignerName() const final; + std::string getSignerSubjectDN() const final; + // Use -1 as validation_time for now + + CertificateValidationStatus validateCertificateResult() final; + void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function &doneCallback) final; + std::unique_ptr getCertificateInfo() const final; + void addData(unsigned char *data_block, int data_len) final; + HashAlgorithm getHashAlgorithm() const final; + + NSSSignatureVerification(const NSSSignatureVerification &) = delete; + NSSSignatureVerification &operator=(const NSSSignatureVerification &) = delete; + +private: + std::vector p7; + NSSCMSMessage *CMSMessage; + NSSCMSSignedData *CMSSignedData; + NSSCMSSignerInfo *CMSSignerInfo; + SECItem CMSitem; + std::unique_ptr hashContext; + std::future validationStatus; + std::optional cachedValidationStatus; +}; + +class NSSSignatureCreation final : public CryptoSign::SigningInterface +{ +public: + NSSSignatureCreation(const std::string &certNickname, HashAlgorithm digestAlgTag); + ~NSSSignatureCreation() final; + std::unique_ptr getCertificateInfo() const final; + void addData(unsigned char *data_block, int data_len) final; + std::variant, CryptoSign::SigningError> signDetached(const std::string &password) final; + CryptoSign::SignatureType signatureType() const final { return CryptoSign::SignatureType::adbe_pkcs7_detached; } + + NSSSignatureCreation(const NSSSignatureCreation &) = delete; + NSSSignatureCreation &operator=(const NSSSignatureCreation &) = delete; + +private: + std::unique_ptr hashContext; + CERTCertificate *signing_cert; +}; + +class POPPLER_PRIVATE_EXPORT NSSSignatureConfiguration +{ +public: + // Initializes the NSS dir with the custom given directory + // calling it with an empty string means use the default firefox db, /etc/pki/nssdb, ~/.pki/nssdb + // If you don't want a custom NSS dir and the default entries are fine for you, not calling this function is fine + // If wanted, this has to be called before doing signature validation calls + static void setNSSDir(const GooString &nssDir); + + // Gets the currently in use NSS dir + static std::string getNSSDir(); + + static void setNSSPasswordCallback(const std::function &f); + + NSSSignatureConfiguration() = delete; + +private: + static std::string sNssDir; +}; + +class NSSCryptoSignBackend final : public CryptoSign::Backend +{ +public: + std::unique_ptr createVerificationHandler(std::vector &&pkcs7, CryptoSign::SignatureType) final; + std::unique_ptr createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) final; + std::vector> getAvailableSigningCertificates() final; + ~NSSCryptoSignBackend() final; +}; + +#endif diff --git a/poppler/NameToCharCode.cc b/poppler/NameToCharCode.cc index 52056a2..a811f92 100644 --- a/poppler/NameToCharCode.cc +++ b/poppler/NameToCharCode.cc @@ -14,6 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2019 Albert Astals Cid +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -23,110 +24,18 @@ #include #include -#include "goo/gmem.h" #include "NameToCharCode.h" -//------------------------------------------------------------------------ - -struct NameToCharCodeEntry -{ - char *name; - CharCode c; -}; - -//------------------------------------------------------------------------ - -NameToCharCode::NameToCharCode() -{ - int i; - - size = 31; - len = 0; - tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry)); - for (i = 0; i < size; ++i) { - tab[i].name = nullptr; - } -} - -NameToCharCode::~NameToCharCode() -{ - int i; - - for (i = 0; i < size; ++i) { - if (tab[i].name) { - gfree(tab[i].name); - } - } - gfree(tab); -} - void NameToCharCode::add(const char *name, CharCode c) { - NameToCharCodeEntry *oldTab; - int h, i, oldSize; - - // expand the table if necessary - if (len >= size / 2) { - oldSize = size; - oldTab = tab; - size = 2 * size + 1; - tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry)); - for (h = 0; h < size; ++h) { - tab[h].name = nullptr; - } - for (i = 0; i < oldSize; ++i) { - if (oldTab[i].name) { - h = hash(oldTab[i].name); - while (tab[h].name) { - if (++h == size) { - h = 0; - } - } - tab[h] = oldTab[i]; - } - } - gfree(oldTab); - } - - // add the new name - h = hash(name); - while (tab[h].name && strcmp(tab[h].name, name)) { - if (++h == size) { - h = 0; - } - } - if (!tab[h].name) { - tab[h].name = copyString(name); - } - tab[h].c = c; - - ++len; + tab.insert_or_assign(std::string { name }, c); } CharCode NameToCharCode::lookup(const char *name) const { - int h; - - h = hash(name); - while (tab[h].name) { - if (!strcmp(tab[h].name, name)) { - return tab[h].c; - } - if (++h == size) { - h = 0; - } + auto it = tab.find(name); + if (it != tab.end()) { + return it->second; } return 0; } - -int NameToCharCode::hash(const char *name) const -{ - const char *p; - unsigned int h; - - h = 0; - for (p = name; *p; ++p) { - h = 17 * h + (int)(*p & 0xff); - } - return (int)(h % size); -} diff --git a/poppler/NameToCharCode.h b/poppler/NameToCharCode.h index 53b8972..7b4b6d8 100644 --- a/poppler/NameToCharCode.h +++ b/poppler/NameToCharCode.h @@ -14,6 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2018, 2019 Albert Astals Cid +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -24,6 +25,8 @@ #define NAMETOCHARCODE_H #include "CharTypes.h" +#include +#include struct NameToCharCodeEntry; @@ -32,21 +35,19 @@ struct NameToCharCodeEntry; class NameToCharCode { public: - NameToCharCode(); - ~NameToCharCode(); - - NameToCharCode(const NameToCharCode &) = delete; - NameToCharCode &operator=(const NameToCharCode &) = delete; - void add(const char *name, CharCode c); CharCode lookup(const char *name) const; private: - int hash(const char *name) const; - - NameToCharCodeEntry *tab; - int size; - int len; + /*This is a helper struct to allow looking up by char* into a std string unordered_map*/ + struct string_hasher + { + using is_transparent = void; + [[nodiscard]] size_t operator()(const char *txt) const { return std::hash {}(txt); } + [[nodiscard]] size_t operator()(const std::string &txt) const { return std::hash {}(txt); } + }; + + std::unordered_map> tab; }; #endif diff --git a/poppler/NameToUnicodeTable.h b/poppler/NameToUnicodeTable.h index c7749f0..b4c29c4 100644 --- a/poppler/NameToUnicodeTable.h +++ b/poppler/NameToUnicodeTable.h @@ -15,7 +15,7 @@ // // Copyright (C) 2011, 2012, 2020 Albert Astals Cid // Copyright (C) 2013 Jason Crain -// Copyright (C) 2022 Vincent Lefevre +// Copyright (C) 2022, 2023 Vincent Lefevre // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -3518,6 +3518,7 @@ static const struct NameToUnicodeTab nameToUnicodeTextTab[] = { { 0x0021, "!" }, { 0x05bd, "siluqhebrew" }, { 0x05bd, "siluqlefthebrew" }, { 0x223c, "similar" }, + { 0x2243, "similarequal" }, { 0x05c2, "sindothebrew" }, { 0x3274, "siosacirclekorean" }, { 0x3214, "siosaparenkorean" }, diff --git a/poppler/Object.cc b/poppler/Object.cc index 430cdf4..7e2c19c 100644 --- a/poppler/Object.cc +++ b/poppler/Object.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2008, 2010, 2012, 2017, 2019 Albert Astals Cid +// Copyright (C) 2008, 2010, 2012, 2017, 2019, 2024 Albert Astals Cid // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Jakub Alba @@ -44,12 +44,12 @@ Object Object::copy() const CHECK_NOT_DEAD; Object obj; - std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); + std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation) switch (type) { case objString: case objHexString: - obj.string = string->copy(); + obj.string = new GooString(string); break; case objName: case objCmd: @@ -76,12 +76,12 @@ Object Object::deepCopy() const CHECK_NOT_DEAD; Object obj; - std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); + std::memcpy(reinterpret_cast(&obj), this, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation) switch (type) { case objString: case objHexString: - obj.string = string->copy(); + obj.string = new GooString(string); break; case objName: case objCmd: diff --git a/poppler/Object.h b/poppler/Object.h index d097510..a2d3962 100644 --- a/poppler/Object.h +++ b/poppler/Object.h @@ -15,7 +15,7 @@ // // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2008 Kees Cook -// Copyright (C) 2008, 2010, 2017-2021 Albert Astals Cid +// Copyright (C) 2008, 2010, 2017-2021, 2023, 2024 Albert Astals Cid // Copyright (C) 2009 Jakub Wilk // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag @@ -25,6 +25,8 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden +// Copyright (C) 2023 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -46,7 +48,7 @@ #include "poppler_private_export.h" #define OBJECT_TYPE_CHECK(wanted_type) \ - if (unlikely(type != wanted_type)) { \ + if (unlikely(type != (wanted_type))) { \ error(errInternal, 0, \ "Call to Object where the object was type {0:d}, " \ "not the expected type {1:d}", \ @@ -55,7 +57,7 @@ } #define OBJECT_2TYPES_CHECK(wanted_type1, wanted_type2) \ - if (unlikely(type != wanted_type1) && unlikely(type != wanted_type2)) { \ + if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2))) { \ error(errInternal, 0, \ "Call to Object where the object was type {0:d}, " \ "not the expected type {1:d} or {2:d}", \ @@ -64,7 +66,7 @@ } #define OBJECT_3TYPES_CHECK(wanted_type1, wanted_type2, wanted_type3) \ - if (unlikely(type != wanted_type1) && unlikely(type != wanted_type2) && unlikely(type != wanted_type3)) { \ + if (unlikely(type != (wanted_type1)) && unlikely(type != (wanted_type2)) && unlikely(type != (wanted_type3))) { \ error(errInternal, 0, \ "Call to Object where the object was type {0:d}, " \ "not the expected type {1:d}, {2:d} or {3:d}", \ @@ -113,6 +115,44 @@ inline bool operator<(const Ref lhs, const Ref rhs) noexcept return lhs.gen < rhs.gen; } +struct RefRecursionChecker +{ + RefRecursionChecker() = default; + + RefRecursionChecker(const RefRecursionChecker &) = delete; + RefRecursionChecker &operator=(const RefRecursionChecker &) = delete; + + bool insert(Ref ref) + { + if (ref == Ref::INVALID()) { + return true; + } + + // insert returns std::pair + // where the bool is whether the insert succeeded + return alreadySeenRefs.insert(ref.num).second; + } + + void remove(Ref ref) { alreadySeenRefs.erase(ref.num); } + +private: + std::set alreadySeenRefs; +}; + +struct RefRecursionCheckerRemover +{ + // Removes ref from c when this object is removed + RefRecursionCheckerRemover(RefRecursionChecker &c, Ref r) : checker(c), ref(r) { } + ~RefRecursionCheckerRemover() { checker.remove(ref); } + + RefRecursionCheckerRemover(const RefRecursionCheckerRemover &) = delete; + RefRecursionCheckerRemover &operator=(const RefRecursionCheckerRemover &) = delete; + +private: + RefRecursionChecker &checker; + Ref ref; +}; + namespace std { template<> @@ -186,18 +226,22 @@ class POPPLER_PRIVATE_EXPORT Object type = objReal; real = realA; } - explicit Object(GooString *stringA) + explicit Object(std::unique_ptr stringA) { assert(stringA); type = objString; - string = stringA; + string = stringA.release(); } - Object(ObjType typeA, GooString *stringA) + explicit Object(std::string &&stringA) + { + type = objString; + string = new GooString(stringA); + } + Object(ObjType typeA, std::string &&stringA) { assert(typeA == objHexString); - assert(stringA); type = typeA; - string = stringA; + string = new GooString(stringA); } Object(ObjType typeA, const char *stringA) { @@ -240,7 +284,7 @@ class POPPLER_PRIVATE_EXPORT Object Object(Object &&other) noexcept { - std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); + std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation) other.type = objDead; } @@ -248,7 +292,7 @@ class POPPLER_PRIVATE_EXPORT Object { free(); - std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); + std::memcpy(reinterpret_cast(this), &other, sizeof(Object)); // NOLINT(bugprone-undefined-memory-manipulation) other.type = objDead; return *this; @@ -492,7 +536,7 @@ class POPPLER_PRIVATE_EXPORT Object const Object &dictGetValNF(int i) const; // Stream accessors. - void streamReset(); + [[nodiscard]] bool streamReset(); void streamClose(); int streamGetChar(); int streamGetChars(int nChars, unsigned char *buffer); @@ -538,141 +582,16 @@ class POPPLER_PRIVATE_EXPORT Object #include "Array.h" -inline int Object::arrayGetLength() const -{ - OBJECT_TYPE_CHECK(objArray); - return array->getLength(); -} - -inline void Object::arrayAdd(Object &&elem) -{ - OBJECT_TYPE_CHECK(objArray); - array->add(std::move(elem)); -} - -inline void Object::arrayRemove(int i) -{ - OBJECT_TYPE_CHECK(objArray); - array->remove(i); -} - -inline Object Object::arrayGet(int i, int recursion = 0) const -{ - OBJECT_TYPE_CHECK(objArray); - return array->get(i, recursion); -} - -inline const Object &Object::arrayGetNF(int i) const -{ - OBJECT_TYPE_CHECK(objArray); - return array->getNF(i); -} - //------------------------------------------------------------------------ // Dict accessors. //------------------------------------------------------------------------ #include "Dict.h" -inline int Object::dictGetLength() const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->getLength(); -} - -inline void Object::dictAdd(const char *key, Object &&val) -{ - OBJECT_TYPE_CHECK(objDict); - dict->add(key, std::move(val)); -} - -inline void Object::dictSet(const char *key, Object &&val) -{ - OBJECT_TYPE_CHECK(objDict); - dict->set(key, std::move(val)); -} - -inline void Object::dictRemove(const char *key) -{ - OBJECT_TYPE_CHECK(objDict); - dict->remove(key); -} - -inline bool Object::dictIs(const char *dictType) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->is(dictType); -} - -inline bool Object::isDict(const char *dictType) const -{ - return type == objDict && dictIs(dictType); -} - -inline Object Object::dictLookup(const char *key, int recursion) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->lookup(key, recursion); -} - -inline const Object &Object::dictLookupNF(const char *key) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->lookupNF(key); -} - -inline const char *Object::dictGetKey(int i) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->getKey(i); -} - -inline Object Object::dictGetVal(int i) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->getVal(i); -} - -inline const Object &Object::dictGetValNF(int i) const -{ - OBJECT_TYPE_CHECK(objDict); - return dict->getValNF(i); -} - //------------------------------------------------------------------------ // Stream accessors. //------------------------------------------------------------------------ #include "Stream.h" -inline void Object::streamReset() -{ - OBJECT_TYPE_CHECK(objStream); - stream->reset(); -} - -inline void Object::streamClose() -{ - OBJECT_TYPE_CHECK(objStream); - stream->close(); -} - -inline int Object::streamGetChar() -{ - OBJECT_TYPE_CHECK(objStream); - return stream->getChar(); -} - -inline int Object::streamGetChars(int nChars, unsigned char *buffer) -{ - OBJECT_TYPE_CHECK(objStream); - return stream->doGetChars(nChars, buffer); -} - -inline Dict *Object::streamGetDict() const -{ - OBJECT_TYPE_CHECK(objStream); - return stream->getDict(); -} - #endif diff --git a/poppler/OptionalContent.cc b/poppler/OptionalContent.cc index 597833d..174c236 100644 --- a/poppler/OptionalContent.cc +++ b/poppler/OptionalContent.cc @@ -9,6 +9,7 @@ // Copyright 2008 Mark Kaplan // Copyright 2018 Adam Reichold // Copyright 2019 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // Released under the GPL (version 2, or later, at your option) // @@ -16,7 +17,6 @@ #include -#include "goo/gmem.h" #include "goo/GooString.h" #include "Error.h" #include "OptionalContent.h" @@ -31,12 +31,12 @@ //------------------------------------------------------------------------ -OCGs::OCGs(Object *ocgObject, XRef *xref) : m_xref(xref) +OCGs::OCGs(const Object &ocgObject, XRef *xref) : m_xref(xref) { // we need to parse the dictionary here, and build optionalContentGroups ok = true; - Object ocgList = ocgObject->dictLookup("OCGs"); + Object ocgList = ocgObject.dictLookup("OCGs"); if (!ocgList.isArray()) { error(errSyntaxError, -1, "Expected the optional content group list, but wasn't able to find it, or it isn't an Array"); ok = false; @@ -60,7 +60,7 @@ OCGs::OCGs(Object *ocgObject, XRef *xref) : m_xref(xref) optionalContentGroups.emplace(ocgRef.getRef(), std::move(thisOptionalContentGroup)); } - Object defaultOcgConfig = ocgObject->dictLookup("D"); + Object defaultOcgConfig = ocgObject.dictLookup("D"); if (!defaultOcgConfig.isDict()) { error(errSyntaxError, -1, "Expected the default config, but wasn't able to find it, or it isn't a Dictionary"); ok = false; @@ -119,13 +119,13 @@ bool OCGs::hasOCGs() const return !(optionalContentGroups.empty()); } -OptionalContentGroup *OCGs::findOcgByRef(const Ref ref) +OptionalContentGroup *OCGs::findOcgByRef(const Ref ref) const { const auto ocg = optionalContentGroups.find(ref); return ocg != optionalContentGroups.end() ? ocg->second.get() : nullptr; } -bool OCGs::optContentIsVisible(const Object *dictRef) +bool OCGs::optContentIsVisible(const Object *dictRef) const { Dict *dict; bool result = true; @@ -184,7 +184,7 @@ bool OCGs::optContentIsVisible(const Object *dictRef) return result; } -bool OCGs::evalOCVisibilityExpr(const Object *expr, int recursion) +bool OCGs::evalOCVisibilityExpr(const Object *expr, int recursion) const { OptionalContentGroup *ocg; bool ret; @@ -231,7 +231,7 @@ bool OCGs::evalOCVisibilityExpr(const Object *expr, int recursion) return ret; } -bool OCGs::allOn(Array *ocgArray) +bool OCGs::allOn(Array *ocgArray) const { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); @@ -245,7 +245,7 @@ bool OCGs::allOn(Array *ocgArray) return true; } -bool OCGs::allOff(Array *ocgArray) +bool OCGs::allOff(Array *ocgArray) const { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); @@ -259,7 +259,7 @@ bool OCGs::allOff(Array *ocgArray) return true; } -bool OCGs::anyOn(Array *ocgArray) +bool OCGs::anyOn(Array *ocgArray) const { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); @@ -273,7 +273,7 @@ bool OCGs::anyOn(Array *ocgArray) return false; } -bool OCGs::anyOff(Array *ocgArray) +bool OCGs::anyOff(Array *ocgArray) const { for (int i = 0; i < ocgArray->getLength(); ++i) { const Object &ocgItem = ocgArray->getNF(i); @@ -295,7 +295,7 @@ OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(nullptr) if (!ocgName.isString()) { error(errSyntaxWarning, -1, "Expected the name of the OCG, but wasn't able to find it, or it isn't a String"); } else { - m_name = new GooString(ocgName.getString()); + m_name = ocgName.getString()->copy(); } viewState = printState = ocUsageUnset; @@ -326,15 +326,9 @@ OptionalContentGroup::OptionalContentGroup(Dict *ocgDict) : m_name(nullptr) } } -OptionalContentGroup::OptionalContentGroup(GooString *label) -{ - m_name = label; - m_state = On; -} - const GooString *OptionalContentGroup::getName() const { - return m_name; + return m_name.get(); } void OptionalContentGroup::setRef(const Ref ref) @@ -347,7 +341,4 @@ Ref OptionalContentGroup::getRef() const return m_ref; } -OptionalContentGroup::~OptionalContentGroup() -{ - delete m_name; -} +OptionalContentGroup::~OptionalContentGroup() = default; diff --git a/poppler/OptionalContent.h b/poppler/OptionalContent.h index 22b9409..0767560 100644 --- a/poppler/OptionalContent.h +++ b/poppler/OptionalContent.h @@ -7,6 +7,7 @@ // Copyright 2013, 2018, 2019, 2021 Albert Astals Cid // Copyright 2018 Adam Reichold // Copyright 2019 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // Released under the GPL (version 2, or later, at your option) // @@ -31,7 +32,7 @@ class OptionalContentGroup; class POPPLER_PRIVATE_EXPORT OCGs { public: - OCGs(Object *ocgObject, XRef *xref); + OCGs(const Object &ocgObject, XRef *xref); OCGs(const OCGs &) = delete; OCGs &operator=(const OCGs &) = delete; @@ -42,21 +43,21 @@ class POPPLER_PRIVATE_EXPORT OCGs bool hasOCGs() const; const std::unordered_map> &getOCGs() const { return optionalContentGroups; } - OptionalContentGroup *findOcgByRef(const Ref ref); + OptionalContentGroup *findOcgByRef(const Ref ref) const; - Array *getOrderArray() { return (order.isArray() && order.arrayGetLength() > 0) ? order.getArray() : nullptr; } - Array *getRBGroupsArray() { return (rbgroups.isArray() && rbgroups.arrayGetLength()) ? rbgroups.getArray() : nullptr; } + const Array *getOrderArray() const { return (order.isArray() && order.arrayGetLength() > 0) ? order.getArray() : nullptr; } + const Array *getRBGroupsArray() const { return (rbgroups.isArray() && rbgroups.arrayGetLength()) ? rbgroups.getArray() : nullptr; } - bool optContentIsVisible(const Object *dictRef); + bool optContentIsVisible(const Object *dictRef) const; private: bool ok; - bool evalOCVisibilityExpr(const Object *expr, int recursion); - bool allOn(Array *ocgArray); - bool allOff(Array *ocgArray); - bool anyOn(Array *ocgArray); - bool anyOff(Array *ocgArray); + bool evalOCVisibilityExpr(const Object *expr, int recursion) const; + bool allOn(Array *ocgArray) const; + bool allOff(Array *ocgArray) const; + bool anyOn(Array *ocgArray) const; + bool anyOff(Array *ocgArray) const; std::unordered_map> optionalContentGroups; @@ -86,8 +87,6 @@ class POPPLER_PRIVATE_EXPORT OptionalContentGroup explicit OptionalContentGroup(Dict *dict); - explicit OptionalContentGroup(GooString *label); - ~OptionalContentGroup(); OptionalContentGroup(const OptionalContentGroup &) = delete; @@ -105,7 +104,7 @@ class POPPLER_PRIVATE_EXPORT OptionalContentGroup UsageState getPrintState() const { return printState; } private: - GooString *m_name; + std::unique_ptr m_name; Ref m_ref; State m_state; UsageState viewState; // suggested state when viewing diff --git a/poppler/Outline.cc b/poppler/Outline.cc index cbb6cb4..abeca39 100644 --- a/poppler/Outline.cc +++ b/poppler/Outline.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Marco Pesenti Gritti -// Copyright (C) 2008, 2016-2019, 2021 Albert Astals Cid +// Copyright (C) 2008, 2016-2019, 2021, 2023 Albert Astals Cid // Copyright (C) 2009 Nick Jones // Copyright (C) 2016 Jason Crain // Copyright (C) 2017 Adrian Johnson @@ -22,6 +22,7 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2021 RM +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -90,8 +91,7 @@ static void insertChildHelper(const std::string &itemTitle, int destPageNum, uns Object outlineItem = Object(new Dict(xref)); - GooString *g = new GooString(itemTitle); - outlineItem.dictSet("Title", Object(g)); + outlineItem.dictSet("Title", Object(std::make_unique(itemTitle))); outlineItem.dictSet("Dest", Object(a)); outlineItem.dictSet("Count", Object(1)); outlineItem.dictAdd("Parent", Object(parentObjRef)); @@ -301,8 +301,7 @@ int Outline::addOutlineTreeNodeList(const std::vector &nodeList } lastRef = outlineItemRef; - GooString *g = new GooString(node.title); - outlineItem.dictSet("Title", Object(g)); + outlineItem.dictSet("Title", Object(std::make_unique(node.title))); outlineItem.dictSet("Dest", Object(a)); itemCount++; @@ -407,15 +406,12 @@ OutlineItem::OutlineItem(const Dict *dict, Ref refA, OutlineItem *parentA, XRef parent = parentA; xref = xrefA; doc = docA; - title = nullptr; kids = nullptr; obj1 = dict->lookup("Title"); if (obj1.isString()) { const GooString *s = obj1.getString(); - titleLen = TextStringToUCS4(s->toStr(), &title); - } else { - titleLen = 0; + title = TextStringToUCS4(s->toStr()); } obj1 = dict->lookup("Dest"); @@ -446,9 +442,6 @@ OutlineItem::~OutlineItem() delete kids; kids = nullptr; } - if (title) { - gfree(title); - } } std::vector *OutlineItem::readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA) @@ -483,19 +476,21 @@ void OutlineItem::open() { if (!kids) { Object itemDict = xref->fetch(ref); - const Object &firstRef = itemDict.dictLookupNF("First"); - kids = readItemList(this, &firstRef, xref, doc); + if (itemDict.isDict()) { + const Object &firstRef = itemDict.dictLookupNF("First"); + kids = readItemList(this, &firstRef, xref, doc); + } else { + kids = new std::vector(); + } } } void OutlineItem::setTitle(const std::string &titleA) { - gfree(title); - Object dict = xref->fetch(ref); - GooString *g = new GooString(titleA); - titleLen = TextStringToUCS4(g->toStr(), &title); - dict.dictSet("Title", Object(g)); + auto g = std::make_unique(titleA); + title = TextStringToUCS4(g->toStr()); + dict.dictSet("Title", Object(std::move(g))); xref->setModifiedObject(&dict, ref); } diff --git a/poppler/Outline.h b/poppler/Outline.h index a301604..877eaac 100644 --- a/poppler/Outline.h +++ b/poppler/Outline.h @@ -17,6 +17,7 @@ // Copyright (C) 2016, 2018, 2021 Albert Astals Cid // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2021 RM +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -27,6 +28,7 @@ #define OUTLINE_H #include +#include #include "Object.h" #include "CharTypes.h" #include "poppler_private_export.h" @@ -91,9 +93,8 @@ class POPPLER_PRIVATE_EXPORT OutlineItem OutlineItem(const OutlineItem &) = delete; OutlineItem &operator=(const OutlineItem &) = delete; static std::vector *readItemList(OutlineItem *parent, const Object *firstItemRef, XRef *xrefA, PDFDoc *docA); - const Unicode *getTitle() const { return title; } + const std::vector &getTitle() const { return title; } void setTitle(const std::string &titleA); - int getTitleLength() const { return titleLen; } bool setPageDest(int i); // OutlineItem keeps the ownership of the action const LinkAction *getAction() const { return action.get(); } @@ -112,8 +113,7 @@ class POPPLER_PRIVATE_EXPORT OutlineItem OutlineItem *parent; PDFDoc *doc; XRef *xref; - Unicode *title; - int titleLen; + std::vector title; std::unique_ptr action; bool startsOpen; std::vector *kids; // nullptr if this item is closed or has no kids diff --git a/poppler/OutputDev.cc b/poppler/OutputDev.cc index a3fa2b2..526f845 100644 --- a/poppler/OutputDev.cc +++ b/poppler/OutputDev.cc @@ -20,6 +20,7 @@ // Copyright (C) 2009, 2012, 2013, 2018, 2019 Albert Astals Cid // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2018 Adam Reichold +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -108,7 +109,9 @@ void OutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int wid int i, j; if (inlineImg) { - str->reset(); + if (!str->reset()) { + return; + } j = height * ((width + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); @@ -122,17 +125,16 @@ void OutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Stream *s drawImageMask(state, ref, str, width, height, invert, false, inlineImg); } -void OutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) -{ - return; -} +void OutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) { } void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { int i, j; if (inlineImg) { - str->reset(); + if (!str->reset()) { + return; + } j = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); diff --git a/poppler/OutputDev.h b/poppler/OutputDev.h index f9c99a7..2a0619f 100644 --- a/poppler/OutputDev.h +++ b/poppler/OutputDev.h @@ -16,10 +16,10 @@ // Copyright (C) 2005 Jonathan Blandford // Copyright (C) 2006 Thorkild Stray // Copyright (C) 2007 Jeff Muizelaar -// Copyright (C) 2007, 2011, 2017, 2021 Adrian Johnson +// Copyright (C) 2007, 2011, 2017, 2021, 2023 Adrian Johnson // Copyright (C) 2009-2013, 2015 Thomas Freitag // Copyright (C) 2009, 2011 Carlos Garcia Campos -// Copyright (C) 2009, 2012, 2013, 2018, 2019, 2021 Albert Astals Cid +// Copyright (C) 2009, 2012, 2013, 2018, 2019, 2021, 2024 Albert Astals Cid // Copyright (C) 2010 Christian Feuersänger // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2012 William Bader @@ -27,6 +27,7 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Philipp Knechtges +// Copyright (C) 2024 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -111,6 +112,9 @@ class POPPLER_PRIVATE_EXPORT OutputDev // box is the crop box? virtual bool needClipToCropBox() { return false; } + // Does this device supports transparency (alpha channel) in JPX streams? + virtual bool supportJPXtransparency() { return false; } + //----- initialization and control // Set default transform matrix. @@ -142,27 +146,27 @@ class POPPLER_PRIVATE_EXPORT OutputDev auto invalidref = Ref::INVALID(); if (defaultGrayProfile) { - auto cs = new GfxICCBasedColorSpace(1, new GfxDeviceGrayColorSpace(), &invalidref); + auto cs = std::make_unique(1, std::make_unique(), &invalidref); cs->setProfile(defaultGrayProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called - state->setDefaultGrayColorSpace(cs); + state->setDefaultGrayColorSpace(std::move(cs)); } if (defaultRGBProfile) { - auto cs = new GfxICCBasedColorSpace(3, new GfxDeviceRGBColorSpace(), &invalidref); + auto cs = std::make_unique(3, std::make_unique(), &invalidref); cs->setProfile(defaultRGBProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called - state->setDefaultRGBColorSpace(cs); + state->setDefaultRGBColorSpace(std::move(cs)); } if (defaultCMYKProfile) { - auto cs = new GfxICCBasedColorSpace(4, new GfxDeviceCMYKColorSpace(), &invalidref); + auto cs = std::make_unique(4, std::make_unique(), &invalidref); cs->setProfile(defaultCMYKProfile); cs->buildTransforms(state); // needs to happen after state->setDisplayProfile has been called - state->setDefaultCMYKColorSpace(cs); + state->setDefaultCMYKColorSpace(std::move(cs)); } #endif } @@ -173,14 +177,8 @@ class POPPLER_PRIVATE_EXPORT OutputDev virtual void cvtDevToUser(double dx, double dy, double *ux, double *uy); virtual void cvtUserToDev(double ux, double uy, int *dx, int *dy); - const double *getDefCTM() const - { - return defCTM; - } - const double *getDefICTM() const - { - return defICTM; - } + const double *getDefCTM() const { return defCTM; } + const double *getDefICTM() const { return defICTM; } //----- save/restore graphics state virtual void saveState(GfxState * /*state*/) { } @@ -240,34 +238,13 @@ class POPPLER_PRIVATE_EXPORT OutputDev { return false; } - virtual bool functionShadedFill(GfxState * /*state*/, GfxFunctionShading * /*shading*/) - { - return false; - } - virtual bool axialShadedFill(GfxState * /*state*/, GfxAxialShading * /*shading*/, double /*tMin*/, double /*tMax*/) - { - return false; - } - virtual bool axialShadedSupportExtend(GfxState * /*state*/, GfxAxialShading * /*shading*/) - { - return false; - } - virtual bool radialShadedFill(GfxState * /*state*/, GfxRadialShading * /*shading*/, double /*sMin*/, double /*sMax*/) - { - return false; - } - virtual bool radialShadedSupportExtend(GfxState * /*state*/, GfxRadialShading * /*shading*/) - { - return false; - } - virtual bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) - { - return false; - } - virtual bool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) - { - return false; - } + virtual bool functionShadedFill(GfxState * /*state*/, GfxFunctionShading * /*shading*/) { return false; } + virtual bool axialShadedFill(GfxState * /*state*/, GfxAxialShading * /*shading*/, double /*tMin*/, double /*tMax*/) { return false; } + virtual bool axialShadedSupportExtend(GfxState * /*state*/, GfxAxialShading * /*shading*/) { return false; } + virtual bool radialShadedFill(GfxState * /*state*/, GfxRadialShading * /*shading*/, double /*sMin*/, double /*sMax*/) { return false; } + virtual bool radialShadedSupportExtend(GfxState * /*state*/, GfxRadialShading * /*shading*/) { return false; } + virtual bool gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading) { return false; } + virtual bool patchMeshShadedFill(GfxState *state, GfxPatchMeshShading *shading) { return false; } //----- path clipping @@ -347,26 +324,20 @@ class POPPLER_PRIVATE_EXPORT OutputDev virtual void type3D1(GfxState * /*state*/, double /*wx*/, double /*wy*/, double /*llx*/, double /*lly*/, double /*urx*/, double /*ury*/) { } //----- form XObjects - virtual void beginForm(Ref /*id*/) { } + virtual void beginForm(Object * /* obj */, Ref /*id*/) { } virtual void drawForm(Ref /*id*/) { } - virtual void endForm(Ref /*id*/) { } + virtual void endForm(Object * /* obj */, Ref /*id*/) { } //----- PostScript XObjects virtual void psXObject(Stream * /*psStream*/, Stream * /*level1Stream*/) { } //----- Profiling void startProfile(); - std::unordered_map *getProfileHash() const - { - return profileHash.get(); - } + std::unordered_map *getProfileHash() const { return profileHash.get(); } std::unique_ptr> endProfile(); //----- transparency groups and soft masks - virtual bool checkTransparencyGroup(GfxState * /*state*/, bool /*knockout*/) - { - return true; - } + virtual bool checkTransparencyGroup(GfxState * /*state*/, bool /*knockout*/) { return true; } virtual void beginTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/, GfxColorSpace * /*blendingColorSpace*/, bool /*isolated*/, bool /*knockout*/, bool /*forSoftMask*/) { } virtual void endTransparencyGroup(GfxState * /*state*/) { } virtual void paintTransparencyGroup(GfxState * /*state*/, const double * /*bbox*/) { } @@ -377,51 +348,21 @@ class POPPLER_PRIVATE_EXPORT OutputDev virtual void processLink(AnnotLink * /*link*/) { } #if 1 //~tmp: turn off anti-aliasing temporarily - virtual bool getVectorAntialias() - { - return false; - } + virtual bool getVectorAntialias() { return false; } virtual void setVectorAntialias(bool /*vaa*/) { } #endif #ifdef USE_CMS - void setDisplayProfile(const GfxLCMSProfilePtr &profile) - { - displayprofile = profile; - } - GfxLCMSProfilePtr getDisplayProfile() const - { - return displayprofile; - } - void setDefaultGrayProfile(const GfxLCMSProfilePtr &profile) - { - defaultGrayProfile = profile; - } - GfxLCMSProfilePtr getDefaultGrayProfile() const - { - return defaultGrayProfile; - } - void setDefaultRGBProfile(const GfxLCMSProfilePtr &profile) - { - defaultRGBProfile = profile; - } - GfxLCMSProfilePtr getDefaultRGBProfile() const - { - return defaultRGBProfile; - } - void setDefaultCMYKProfile(const GfxLCMSProfilePtr &profile) - { - defaultCMYKProfile = profile; - } - GfxLCMSProfilePtr getDefaultCMYKProfile() const - { - return defaultCMYKProfile; - } - - PopplerCache *getIccColorSpaceCache() - { - return &iccColorSpaceCache; - } + void setDisplayProfile(const GfxLCMSProfilePtr &profile) { displayprofile = profile; } + GfxLCMSProfilePtr getDisplayProfile() const { return displayprofile; } + void setDefaultGrayProfile(const GfxLCMSProfilePtr &profile) { defaultGrayProfile = profile; } + GfxLCMSProfilePtr getDefaultGrayProfile() const { return defaultGrayProfile; } + void setDefaultRGBProfile(const GfxLCMSProfilePtr &profile) { defaultRGBProfile = profile; } + GfxLCMSProfilePtr getDefaultRGBProfile() const { return defaultRGBProfile; } + void setDefaultCMYKProfile(const GfxLCMSProfilePtr &profile) { defaultCMYKProfile = profile; } + GfxLCMSProfilePtr getDefaultCMYKProfile() const { return defaultCMYKProfile; } + + PopplerCache *getIccColorSpaceCache() { return &iccColorSpaceCache; } #endif private: diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc index a3c01e6..f0dae9b 100644 --- a/poppler/PDFDoc.cc +++ b/poppler/PDFDoc.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005, 2006, 2008 Brad Hards -// Copyright (C) 2005, 2007-2009, 2011-2022 Albert Astals Cid +// Copyright (C) 2005, 2007-2009, 2011-2024 Albert Astals Cid // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2008, 2010, 2011 Carlos Garcia Campos @@ -46,7 +46,7 @@ // Copyright (C) 2020 Nelson Benítez León // Copyright (C) 2020 Thorsten Behrens // Copyright (C) 2020 Adam Sampson -// Copyright (C) 2021, 2022 Oliver Sander +// Copyright (C) 2021-2024 Oliver Sander // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 RM // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. @@ -54,6 +54,10 @@ // Copyright (C) 2022 Felix Jung // Copyright (C) 2022 crt // Copyright (C) 2022 Erich E. Hoover +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Vincent Lefevre +// Copyright (C) 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden +// Copyright (C) 2025 Juraj Šarinay // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -63,6 +67,7 @@ #include #include +#include #include #include #include @@ -76,11 +81,11 @@ #include #include #include +#include "CryptoSignBackend.h" #include "goo/glibc.h" #include "goo/gstrtod.h" #include "goo/GooString.h" #include "goo/gfile.h" -#include "poppler-config.h" #include "GlobalParams.h" #include "Page.h" #include "Catalog.h" @@ -98,11 +103,19 @@ #include "PDFDoc.h" #include "Hints.h" #include "UTF.h" +#include "FlateEncoder.h" #include "JSInfo.h" #include "ImageEmbeddingUtils.h" //------------------------------------------------------------------------ +struct FILECloser +{ + void operator()(FILE *f) { fclose(f); } +}; + +//------------------------------------------------------------------------ + #define headerSearchSize \ 1024 // read this many bytes at beginning of // file to look for '%PDF' @@ -123,22 +136,19 @@ #define pdfdocLocker() const std::scoped_lock locker(mutex) -PDFDoc::PDFDoc() { } +PDFDoc::PDFDoc() = default; -PDFDoc::PDFDoc(std::unique_ptr &&fileNameA, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA, const std::function &xrefReconstructedCallback) - : fileName(std::move(fileNameA)), guiData(guiDataA) +PDFDoc::PDFDoc(std::unique_ptr &&fileNameA, const std::optional &ownerPassword, const std::optional &userPassword, const std::function &xrefReconstructedCallback) : fileName(std::move(fileNameA)) { #ifdef _WIN32 const int n = fileName->getLength(); - fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t)); for (int i = 0; i < n; ++i) { - fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff); + fileNameU.push_back((wchar_t)(fileName->getChar(i) & 0xff)); } - fileNameU[n] = L'\0'; - wchar_t *wFileName = (wchar_t *)utf8ToUtf16(fileName->c_str()); + std::u16string u16fileName = utf8ToUtf16(fileName->toStr()); + wchar_t *wFileName = (wchar_t *)u16fileName.data(); file = GooFile::open(wFileName); - gfree(wFileName); #else file = GooFile::open(fileName->toStr()); #endif @@ -160,26 +170,24 @@ PDFDoc::PDFDoc(std::unique_ptr &&fileNameA, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA, const std::function &xrefReconstructedCallback) : guiData(guiDataA) +PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword, const std::optional &userPassword, const std::function &xrefReconstructedCallback) { OSVERSIONINFO version; // save both Unicode and 8-bit copies of the file name - GooString *fileNameG = new GooString(); - fileNameU = (wchar_t *)gmallocn(fileNameLen + 1, sizeof(wchar_t)); + std::unique_ptr fileNameG = std::make_unique(); for (int i = 0; i < fileNameLen; ++i) { fileNameG->append((char)fileNameA[i]); - fileNameU[i] = fileNameA[i]; + fileNameU.push_back(fileNameA[i]); } - fileName.reset(fileNameG); - fileNameU[fileNameLen] = L'\0'; + fileName = std::move(fileNameG); // try to open file // NB: _wfopen is only available in NT version.dwOSVersionInfoSize = sizeof(version); GetVersionEx(&version); if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) { - file = GooFile::open(fileNameU); + file = GooFile::open(fileNameU.c_str()); } else { file = GooFile::open(fileName->toStr()); } @@ -196,17 +204,15 @@ PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA, const std::function &xrefReconstructedCallback) : guiData(guiDataA) +PDFDoc::PDFDoc(BaseStream *strA, const std::optional &ownerPassword, const std::optional &userPassword, const std::function &xrefReconstructedCallback) { if (strA->getFileName()) { - fileName.reset(strA->getFileName()->copy()); + fileName = strA->getFileName()->copy(); #ifdef _WIN32 const int n = fileName->getLength(); - fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t)); for (int i = 0; i < n; ++i) { - fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff); + fileNameU.push_back((wchar_t)(fileName->getChar(i) & 0xff)); } - fileNameU[n] = L'\0'; #endif } str = strA; @@ -230,7 +236,11 @@ bool PDFDoc::setup(const std::optional &ownerPassword, const std::opt return false; } - str->reset(); + if (!str->reset()) { + error(errSyntaxError, -1, "Document base stream reset failure"); + errCode = errFileIO; + return false; + } // check footer // Adobe does not seem to enforce %%EOF, so we do the same @@ -289,14 +299,6 @@ bool PDFDoc::setup(const std::optional &ownerPassword, const std::opt PDFDoc::~PDFDoc() { - if (pageCache) { - for (int i = 0; i < getNumPages(); i++) { - if (pageCache[i]) { - delete pageCache[i]; - } - } - gfree(pageCache); - } delete secHdlr; delete outline; delete catalog; @@ -304,9 +306,6 @@ PDFDoc::~PDFDoc() delete hints; delete linearization; delete str; -#ifdef _WIN32 - gfree(fileNameU); -#endif } // Check for a %%EOF at the end of this stream @@ -482,34 +481,46 @@ static PDFSubtypeConformance pdfConformanceFromString(const std::string &pdfsubv { const std::regex regex("PDF/(?:A|X|VT|E|UA)-[[:digit:]]([[:alpha:]]+)"); std::smatch match; - PDFSubtypeConformance pdfConf = subtypeConfNone; // match contains the PDF conformance (A, B, G, N, P, PG or U) if (std::regex_search(pdfsubver, match, regex)) { - GooString *conf = new GooString(match.str(1)); // Convert to lowercase as the conformance may appear in both cases - conf->lowerCase(); - if (conf->cmp("a") == 0) { - pdfConf = subtypeConfA; - } else if (conf->cmp("b") == 0) { - pdfConf = subtypeConfB; - } else if (conf->cmp("g") == 0) { - pdfConf = subtypeConfG; - } else if (conf->cmp("n") == 0) { - pdfConf = subtypeConfN; - } else if (conf->cmp("p") == 0) { - pdfConf = subtypeConfP; - } else if (conf->cmp("pg") == 0) { - pdfConf = subtypeConfPG; - } else if (conf->cmp("u") == 0) { - pdfConf = subtypeConfU; - } else { - pdfConf = subtypeConfNone; + std::string conf = GooString::toLowerCase(match.str(1)); + switch (conf.size()) { + case 1: { + switch (conf[0]) { + case 'a': + return subtypeConfA; + case 'b': + return subtypeConfB; + case 'g': + return subtypeConfG; + case 'n': + return subtypeConfN; + case 'p': + return subtypeConfP; + case 'u': + return subtypeConfU; + default: + /**/ + break; + } + break; + } + case 2: { + if (conf == std::string_view("pq")) { + return subtypeConfPG; + break; + } } - delete conf; + default: + /**/ + break; + } + error(errSyntaxWarning, -1, "Unexpected pdf subtype {0:s}", conf.c_str()); } - return pdfConf; + return subtypeConfNone; } void PDFDoc::extractPDFSubtype() @@ -584,7 +595,7 @@ std::vector PDFDoc::getSignatureFields() if (fw->getType() == formSignature) { assert(fw->getField()->getType() == formSignature); FormFieldSignature *ffs = static_cast(fw->getField()); - if (std::find(res.begin(), res.end(), ffs) == res.end()) { + if (std::ranges::find(res, ffs) == res.end()) { res.push_back(ffs); } } @@ -707,12 +718,9 @@ bool PDFDoc::isLinearized(bool tryingToReconstruct) } } -void PDFDoc::setDocInfoStringEntry(const char *key, GooString *value) +void PDFDoc::setDocInfoStringEntry(const char *key, std::unique_ptr value) { - bool removeEntry = !value || value->getLength() == 0 || value->hasJustUnicodeMarker(); - if (removeEntry) { - delete value; - } + bool removeEntry = !value || value->getLength() == 0 || (value->toStr() == unicodeByteOrderMark); Object infoObj = getDocInfo(); if (infoObj.isNull() && removeEntry) { @@ -725,7 +733,7 @@ void PDFDoc::setDocInfoStringEntry(const char *key, GooString *value) if (removeEntry) { infoObj.dictSet(key, Object(objNull)); } else { - infoObj.dictSet(key, Object(value)); + infoObj.dictSet(key, Object(std::move(value))); } if (infoObj.dictGetLength() == 0) { @@ -748,7 +756,7 @@ std::unique_ptr PDFDoc::getDocInfoStringEntry(const char *key) return {}; } - return std::unique_ptr(entryObj.getString()->copy()); + return entryObj.getString()->copy(); } static bool get_id(const GooString *encodedidstring, GooString *id) @@ -818,8 +826,6 @@ Hints *PDFDoc::getHints() int PDFDoc::savePageAs(const GooString &name, int pageNo) { FILE *f; - OutStream *outStr; - XRef *yRef, *countRef; if (file && file->modificationTimeChangedSinceOpen()) { return errFileChangedSinceOpen; @@ -852,26 +858,28 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) error(errIO, -1, "Couldn't open file '{0:t}'", &name); return errOpenFile; } - outStr = new FileOutStream(f, 0); + // Calls fclose on f when the fileCloser is destroyed because it goes out of scope + const std::unique_ptr fileCloser(f); + const std::unique_ptr outStr = std::make_unique(f, 0); - yRef = new XRef(getXRef()->getTrailerDict()); + const std::unique_ptr yRef = std::make_unique(getXRef()->getTrailerDict()); if (secHdlr != nullptr && !secHdlr->isUnencrypted()) { yRef->setEncryption(secHdlr->getPermissionFlags(), secHdlr->getOwnerPasswordOk(), fileKey, keyLength, secHdlr->getEncVersion(), secHdlr->getEncRevision(), encAlgorithm); } - countRef = new XRef(); + const std::unique_ptr countRef = std::make_unique(); Object *trailerObj = getXRef()->getTrailerDict(); if (trailerObj->isDict()) { - markPageObjects(trailerObj->getDict(), yRef, countRef, 0, refPage->num, rootNum + 2); + markPageObjects(trailerObj->getDict(), yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } yRef->add(0, 65535, 0, false); - writeHeader(outStr, getPDFMajorVersion(), getPDFMinorVersion()); + writeHeader(outStr.get(), getPDFMajorVersion(), getPDFMinorVersion()); // get and mark info dict Object infoObj = getXRef()->getDocInfo(); if (infoObj.isDict()) { Dict *infoDict = infoObj.getDict(); - markPageObjects(infoDict, yRef, countRef, 0, refPage->num, rootNum + 2); + markPageObjects(infoDict, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); if (trailerObj->isDict()) { Dict *trailerDict = trailerObj->getDict(); const Object &ref = trailerDict->lookupNF("Info"); @@ -887,48 +895,48 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) // get and mark output intents etc. Object catObj = getXRef()->getCatalog(); if (!catObj.isDict()) { - fclose(f); - delete yRef; - delete countRef; - delete outStr; error(errSyntaxError, -1, "XRef's Catalog is not a dictionary"); return errOpenFile; } Dict *catDict = catObj.getDict(); Object pagesObj = catDict->lookup("Pages"); + if (!pagesObj.isDict()) { + error(errSyntaxError, -1, "Catalog Pages is not a dictionary"); + return errOpenFile; + } Object afObj = catDict->lookupNF("AcroForm").copy(); if (!afObj.isNull()) { - markAcroForm(&afObj, yRef, countRef, 0, refPage->num, rootNum + 2); + markAcroForm(&afObj, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } Dict *pagesDict = pagesObj.getDict(); Object resourcesObj = pagesDict->lookup("Resources"); if (resourcesObj.isDict()) { - markPageObjects(resourcesObj.getDict(), yRef, countRef, 0, refPage->num, rootNum + 2); + markPageObjects(resourcesObj.getDict(), yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } - if (!markPageObjects(catDict, yRef, countRef, 0, refPage->num, rootNum + 2)) { - fclose(f); - delete yRef; - delete countRef; - delete outStr; + if (!markPageObjects(catDict, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2)) { error(errSyntaxError, -1, "markPageObjects failed"); return errDamaged; } + if (!page.isDict()) { + error(errSyntaxError, -1, "page is not a dictionary"); + return errOpenFile; + } Dict *pageDict = page.getDict(); if (resourcesObj.isNull() && !pageDict->hasKey("Resources")) { Object *resourceDictObject = getCatalog()->getPage(pageNo)->getResourceDictObject(); if (resourceDictObject->isDict()) { resourcesObj = resourceDictObject->copy(); - markPageObjects(resourcesObj.getDict(), yRef, countRef, 0, refPage->num, rootNum + 2); + markPageObjects(resourcesObj.getDict(), yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } } - markPageObjects(pageDict, yRef, countRef, 0, refPage->num, rootNum + 2); + markPageObjects(pageDict, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); Object annotsObj = pageDict->lookupNF("Annots").copy(); if (!annotsObj.isNull()) { - markAnnotations(&annotsObj, yRef, countRef, 0, refPage->num, rootNum + 2); + markAnnotations(&annotsObj, yRef.get(), countRef.get(), 0, refPage->num, rootNum + 2); } yRef->markUnencrypted(); - writePageObjects(outStr, yRef, 0); + writePageObjects(outStr.get(), yRef.get(), 0); yRef->add(rootNum, 0, outStr->getPos(), true); outStr->printf("%d 0 obj\n", rootNum); @@ -941,7 +949,7 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) } Object value = catDict->getValNF(j).copy(); outStr->printf("/%s ", key); - writeObject(&value, outStr, getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); + writeObject(&value, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } } outStr->printf(">>\nendobj\n"); @@ -951,7 +959,7 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) outStr->printf("<< /Type /Pages /Kids [ %d 0 R ] /Count 1 ", rootNum + 2); if (resourcesObj.isDict()) { outStr->printf("/Resources "); - writeObject(&resourcesObj, outStr, getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); + writeObject(&resourcesObj, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } outStr->printf(">>\n"); outStr->printf("endobj\n"); @@ -969,7 +977,7 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) outStr->printf("/Parent %d 0 R", rootNum + 1); } else { outStr->printf("/%s ", key); - writeObject(&value, outStr, getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); + writeObject(&value, outStr.get(), getXRef(), 0, nullptr, cryptRC4, 0, 0, 0); } } outStr->printf(" >>\nendobj\n"); @@ -979,13 +987,9 @@ int PDFDoc::savePageAs(const GooString &name, int pageNo) ref.num = rootNum; ref.gen = 0; Object trailerDict = createTrailerDict(rootNum + 3, false, 0, &ref, getXRef(), name.c_str(), uxrefOffset); - writeXRefTableTrailer(std::move(trailerDict), yRef, false /* do not write unnecessary entries */, uxrefOffset, outStr, getXRef()); + writeXRefTableTrailer(std::move(trailerDict), yRef.get(), false /* do not write unnecessary entries */, uxrefOffset, outStr.get(), getXRef()); outStr->close(); - fclose(f); - delete yRef; - delete countRef; - delete outStr; return errNone; } @@ -1047,42 +1051,50 @@ int PDFDoc::saveWithoutChangesAs(const GooString &name) int PDFDoc::saveWithoutChangesAs(OutStream *outStr) { - int c; - if (file && file->modificationTimeChangedSinceOpen()) { return errFileChangedSinceOpen; } - BaseStream *copyStr = str->copy(); - copyStr->reset(); - while ((c = copyStr->getChar()) != EOF) { - outStr->put(c); + std::unique_ptr copyStr { str->copy() }; + if (!copyStr->reset()) { + return errFileIO; + } + while (copyStr->lookChar() != EOF) { + std::array array; + size_t size = copyStr->doGetChars(array.size(), array.data()); + auto sizeWritten = outStr->write(std::span(array.data(), size)); + if (size != sizeWritten) { + return errFileIO; + } } copyStr->close(); - delete copyStr; return errNone; } void PDFDoc::saveIncrementalUpdate(OutStream *outStr) { - XRef *uxref; - int c; // copy the original file - BaseStream *copyStr = str->copy(); - copyStr->reset(); - while ((c = copyStr->getChar()) != EOF) { - outStr->put(c); + std::unique_ptr copyStr { str->copy() }; + if (!copyStr->reset()) { + // some err; + } + while (copyStr->lookChar() != EOF) { + std::array array; + size_t size = copyStr->doGetChars(array.size(), array.data()); + auto sizeWritten = outStr->write(std::span(array.data(), size)); + if (size != sizeWritten) { + // Write error of some sort + } } copyStr->close(); - delete copyStr; unsigned char *fileKey; CryptAlgorithm encAlgorithm; int keyLength; xref->getEncryptionParameters(&fileKey, &encAlgorithm, &keyLength); - uxref = new XRef(); + XRef *uxref = new XRef(); uxref->add(0, 65535, 0, false); xref->lock(); for (int i = 0; i < xref->getNumObjects(); i++) { @@ -1200,6 +1212,23 @@ void PDFDoc::saveCompleteRewrite(OutStream *outStr) delete uxref; } +std::string PDFDoc::sanitizedName(const std::string &name) +{ + std::string sanitizedName; + + for (const auto c : name) { + if (c <= (char)0x20 || c >= (char)0x7f || c == ' ' || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%' || c == '#') { + char buf[8]; + sprintf(buf, "#%02x", c & 0xff); + sanitizedName.append(buf); + } else { + sanitizedName.push_back(c); + } + } + + return sanitizedName; +} + void PDFDoc::writeDictionary(Dict *dict, OutStream *outStr, XRef *xRef, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref, std::set *alreadyWrittenDicts) { bool deleteSet = false; @@ -1221,9 +1250,7 @@ void PDFDoc::writeDictionary(Dict *dict, OutStream *outStr, XRef *xRef, unsigned outStr->printf("<<"); for (int i = 0; i < dict->getLength(); i++) { GooString keyName(dict->getKey(i)); - GooString *keyNameToPrint = keyName.sanitizedName(); - outStr->printf("/%s ", keyNameToPrint->c_str()); - delete keyNameToPrint; + outStr->printf("/%s ", sanitizedName(keyName.toStr()).c_str()); Object obj1 = dict->getValNF(i).copy(); writeObject(&obj1, outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); } @@ -1236,8 +1263,10 @@ void PDFDoc::writeDictionary(Dict *dict, OutStream *outStr, XRef *xRef, unsigned void PDFDoc::writeStream(Stream *str, OutStream *outStr) { + if (!str->reset()) { + return; + } outStr->printf("stream\r\n"); - str->reset(); for (int c = str->getChar(); c != EOF; c = str->getChar()) { outStr->printf("%c", c); } @@ -1260,7 +1289,10 @@ void PDFDoc::writeRawStream(Stream *str, OutStream *outStr) } outStr->printf("stream\r\n"); - str->unfilteredReset(); + if (!str->unfilteredReset()) { + error(errSyntaxError, -1, "PDFDoc::writeRawStream, reset failed"); + return; + } for (Goffset i = 0; i < length; i++) { int c = str->getUnfilteredChar(); if (unlikely(c == EOF)) { @@ -1269,29 +1301,31 @@ void PDFDoc::writeRawStream(Stream *str, OutStream *outStr) } outStr->printf("%c", c); } - str->reset(); + (void)str->reset(); outStr->printf("\r\nendstream\r\n"); } void PDFDoc::writeString(const GooString *s, OutStream *outStr, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref) { // Encrypt string if encryption is enabled - GooString *sEnc = nullptr; + std::unique_ptr sEnc = nullptr; if (fileKey) { EncryptStream *enc = new EncryptStream(new MemStream(s->c_str(), 0, s->getLength(), Object(objNull)), fileKey, encAlgorithm, keyLength, ref); - sEnc = new GooString(); + sEnc = std::make_unique(); int c; - enc->reset(); + if (!enc->reset()) { + return; + } while ((c = enc->getChar()) != EOF) { sEnc->append((char)c); } delete enc; - s = sEnc; + s = sEnc.get(); } // Write data - if (s->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(s->toStr())) { // unicode string don't necessary end with \0 const char *c = s->c_str(); std::stringstream stream; @@ -1321,8 +1355,6 @@ void PDFDoc::writeString(const GooString *s, OutStream *outStr, const unsigned c } outStr->printf(") "); } - - delete sEnc; } Goffset PDFDoc::writeObjectHeader(Ref *ref, OutStream *outStr) @@ -1371,9 +1403,7 @@ void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned in } case objName: { GooString name(obj->getName()); - GooString *nameToPrint = name.sanitizedName(); - outStr->printf("/%s ", nameToPrint->c_str()); - delete nameToPrint; + outStr->printf("/%s ", sanitizedName(name.toStr()).c_str()); break; } case objNull: @@ -1399,8 +1429,8 @@ void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned in // we write the stream unencoded => TODO: write stream encoder // Encrypt stream - EncryptStream *encStream = nullptr; bool removeFilter = true; + bool addEncryptstream = false; if (stream->getKind() == strWeird && fileKey) { Object filter = stream->getDict()->lookup("Filter"); if (!filter.isName("Crypt")) { @@ -1413,25 +1443,35 @@ void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned in } } if (removeFilter) { - encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, ref); - encStream->setAutoDelete(false); - stream = encStream; + addEncryptstream = true; } } else { - encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, ref); - encStream->setAutoDelete(false); - stream = encStream; + addEncryptstream = true; } } else { removeFilter = false; } } else if (fileKey != nullptr) { // Encrypt stream - encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, ref); + addEncryptstream = true; + } + + std::unique_ptr encStream; + std::unique_ptr compressStream; + Object filter = stream->getDict()->lookup("Filter"); + if (filter.isName("FlateDecode")) { + compressStream = std::make_unique(stream); + stream = compressStream.get(); + removeFilter = false; + } + if (addEncryptstream) { + encStream = std::make_unique(stream, fileKey, encAlgorithm, keyLength, ref); encStream->setAutoDelete(false); - stream = encStream; + stream = encStream.get(); } - stream->reset(); + if (!stream->reset()) { + break; + } // recalculate stream length Goffset tmp = 0; for (int c = stream->getChar(); c != EOF; c = stream->getChar()) { @@ -1451,7 +1491,6 @@ void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned in writeDictionary(stream->getDict(), outStr, xRef, numOffset, fileKey, encAlgorithm, keyLength, ref, alreadyWrittenDicts); writeStream(stream, outStr); - delete encStream; } else if (fileKey != nullptr && stream->getKind() == strFile && static_cast(stream)->getNeedsEncryptionOnSave()) { EncryptStream *encStream = new EncryptStream(stream, fileKey, encAlgorithm, keyLength, ref); encStream->setAutoDelete(false); @@ -1492,7 +1531,7 @@ void PDFDoc::writeObject(Object *obj, OutStream *outStr, XRef *xRef, unsigned in outStr->printf("none\r\n"); break; default: - error(errUnimplemented, -1, "Unhandled objType : {0:d}, please report a bug with a testcase\r\n", obj->getType()); + error(errUnimplemented, -1, "Unhandled objType : {0:d}, please report a bug with a testcase", obj->getType()); break; } } @@ -1563,14 +1602,14 @@ Object PDFDoc::createTrailerDict(int uxrefSize, bool incrUpdate, Goffset startxR Array *array = new Array(xRef); // Get the first part of the ID array->add(obj4.arrayGet(0)); - array->add(Object(new GooString((const char *)digest, 16))); + array->add(Object(std::make_unique((const char *)digest, 16))); trailerDict->set("ID", Object(array)); } } else { // new file => same values for the two identifiers Array *array = new Array(xRef); - array->add(Object(new GooString((const char *)digest, 16))); - array->add(Object(new GooString((const char *)digest, 16))); + array->add(Object(std::make_unique((const char *)digest, 16))); + array->add(Object(std::make_unique((const char *)digest, 16))); trailerDict->set("ID", Object(array)); } @@ -1625,7 +1664,9 @@ void PDFDoc::writeXRefTableTrailer(Goffset uxrefOffset, XRef *uxref, bool writeA // file size (doesn't include the trailer) unsigned int fileSize = 0; int c; - str->reset(); + if (!str->reset()) { + return; + } while ((c = str->getChar()) != EOF) { fileSize++; } @@ -1924,7 +1965,6 @@ void PDFDoc::markAcroForm(Object *afObj, XRef *xRef, XRef *countRef, unsigned in getXRef()->setModifiedObject(&acroform, afObj->getRef()); } } - return; } unsigned int PDFDoc::writePageObjects(OutStream *outStr, XRef *xRef, unsigned int numOffset, bool combine) @@ -2091,7 +2131,7 @@ int PDFDoc::getNumPages() return catalog->getNumPages(); } -Page *PDFDoc::parsePage(int page) +std::unique_ptr PDFDoc::parsePage(int page) { Ref pageRef; @@ -2115,7 +2155,7 @@ Page *PDFDoc::parsePage(int page) } Dict *pageDict = obj.getDict(); - return new Page(this, page, std::move(obj), pageRef, new PageAttrs(nullptr, pageDict), catalog->getForm()); + return std::make_unique(this, page, std::move(obj), pageRef, std::make_unique(nullptr, pageDict), catalog->getForm()); } Page *PDFDoc::getPage(int page) @@ -2126,17 +2166,14 @@ Page *PDFDoc::getPage(int page) if (isLinearized() && checkLinearization()) { pdfdocLocker(); - if (!pageCache) { - pageCache = (Page **)gmallocn(getNumPages(), sizeof(Page *)); - for (int i = 0; i < getNumPages(); i++) { - pageCache[i] = nullptr; - } + if (pageCache.empty()) { + pageCache.resize(getNumPages()); } if (!pageCache[page - 1]) { pageCache[page - 1] = parsePage(page); } if (pageCache[page - 1]) { - return pageCache[page - 1]; + return pageCache[page - 1].get(); } else { error(errSyntaxWarning, -1, "Failed parsing page {0:d} using hint tables", page); } @@ -2152,35 +2189,29 @@ bool PDFDoc::hasJavascript() return jsInfo.containsJS(); } -bool PDFDoc::sign(const char *saveFilename, const char *certNickname, const char *password, GooString *partialFieldName, int page, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, - double fontSize, double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, const GooString *reason, - const GooString *location, const std::string &imagePath, const std::optional &ownerPassword, const std::optional &userPassword) +std::optional PDFDoc::createSignature(::Page *destPage, std::unique_ptr &&partialFieldName, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, + double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, + const std::string &imagePath) { - ::Page *destPage = getPage(page); if (destPage == nullptr) { - return false; + return std::nullopt; } + Ref imageResourceRef = Ref::INVALID(); if (!imagePath.empty()) { imageResourceRef = ImageEmbeddingUtils::embed(xref, imagePath); if (imageResourceRef == Ref::INVALID()) { - return false; + return std::nullopt; } } Form *form = catalog->getCreateForm(); - std::string pdfFontName = form->findFontInDefaultResources("Helvetica", ""); - if (pdfFontName.empty()) { - pdfFontName = form->addFontToDefaultResources("Helvetica", "").fontName; - } - - const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) }; Object annotObj = Object(new Dict(getXRef())); annotObj.dictSet("Type", Object(objName, "Annot")); annotObj.dictSet("Subtype", Object(objName, "Widget")); annotObj.dictSet("FT", Object(objName, "Sig")); - annotObj.dictSet("T", Object(partialFieldName)); + annotObj.dictSet("T", Object(std::move(partialFieldName))); Array *rectArray = new Array(getXRef()); rectArray->add(Object(rect.x1)); rectArray->add(Object(rect.y1)); @@ -2188,59 +2219,92 @@ bool PDFDoc::sign(const char *saveFilename, const char *certNickname, const char rectArray->add(Object(rect.y2)); annotObj.dictSet("Rect", Object(rectArray)); - const std::string daStr = da.toAppearanceString(); - annotObj.dictSet("DA", Object(new GooString(daStr))); + if (signatureText.getLength() || signatureTextLeft.getLength()) { + const std::string pdfFontName = form->findPdfFontNameToUseForSigning(); + if (pdfFontName.empty()) { + return std::nullopt; + } + + const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) }; + const std::string daStr = da.toAppearanceString(); + annotObj.dictSet("DA", Object(std::make_unique(daStr))); + + form->ensureFontsForAllCharacters(&signatureText, pdfFontName); + form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName); + } const Ref ref = getXRef()->addIndirectObject(annotObj); catalog->addFormToAcroForm(ref); - // say that there a now signatures and that we should append only - catalog->getAcroForm()->dictSet("SigFlags", Object(3)); - form->ensureFontsForAllCharacters(&signatureText, pdfFontName); - form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName); + catalog->setAcroFormModified(); - std::unique_ptr<::FormFieldSignature> field = std::make_unique<::FormFieldSignature>(this, Object(annotObj.getDict()), ref, nullptr, nullptr); + std::unique_ptr<::FormFieldSignature> field = std::make_unique<::FormFieldSignature>(this, std::move(annotObj), ref, nullptr, nullptr); field->setCustomAppearanceContent(signatureText); field->setCustomAppearanceLeftContent(signatureTextLeft); field->setCustomAppearanceLeftFontSize(leftFontSize); field->setImageResource(imageResourceRef); Object refObj(ref); - AnnotWidget *signatureAnnot = new AnnotWidget(this, &annotObj, &refObj, field.get()); - signatureAnnot->setFlags(signatureAnnot->getFlags() | Annot::flagPrint | Annot::flagLocked | Annot::flagNoRotate); + AnnotWidget *signatureAnnot = new AnnotWidget(this, field->getObj(), &refObj, field.get()); + signatureAnnot->setFlags(signatureAnnot->getFlags() | Annot::flagPrint | /*Annot::flagLocked | TODO */ Annot::flagNoRotate); Dict dummy(getXRef()); auto appearCharacs = std::make_unique(&dummy); appearCharacs->setBorderColor(std::move(borderColor)); appearCharacs->setBackColor(std::move(backgroundColor)); signatureAnnot->setAppearCharacs(std::move(appearCharacs)); + std::unique_ptr border(new AnnotBorderArray()); + border->setWidth(borderWidth); + signatureAnnot->setBorder(std::move(border)); + signatureAnnot->generateFieldAppearance(); signatureAnnot->updateAppearanceStream(); FormWidget *formWidget = field->getWidget(field->getNumWidgets() - 1); formWidget->setWidgetAnnotation(signatureAnnot); - destPage->addAnnot(signatureAnnot); + return SignatureData { { ref.num, ref.gen }, signatureAnnot, formWidget, std::move(field) }; +} - std::unique_ptr border(new AnnotBorderArray()); - border->setWidth(borderWidth); - signatureAnnot->setBorder(std::move(border)); +std::optional PDFDoc::sign(const std::string &saveFilename, const std::string &certNickname, const std::string &password, std::unique_ptr &&partialFieldName, int page, const PDFRectangle &rect, + const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, + std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, const GooString *reason, const GooString *location, const std::string &imagePath, + const std::optional &ownerPassword, const std::optional &userPassword) +{ + ::Page *destPage = getPage(page); + if (destPage == nullptr) { + return CryptoSign::SigningError::InternalError; + } + + std::optional sig = + createSignature(destPage, std::move(partialFieldName), rect, signatureText, signatureTextLeft, fontSize, leftFontSize, std::move(fontColor), borderWidth, std::move(borderColor), std::move(backgroundColor), imagePath); + + if (!sig) { + return CryptoSign::SigningError::GenericError; /*This should probably be expanded with error handling from createSignature*/ + } - FormWidgetSignature *fws = dynamic_cast(formWidget); + sig->annotWidget->setFlags(sig->annotWidget->getFlags() | Annot::flagLocked); + + // say that there a now signatures and that we should append only + catalog->getAcroForm()->dictSet("SigFlags", Object(3)); + + destPage->addAnnot(sig->annotWidget); + + FormWidgetSignature *fws = dynamic_cast(sig->formWidget); if (fws) { - const bool res = fws->signDocument(saveFilename, certNickname, "SHA256", password, reason, location, ownerPassword, userPassword); + const auto res = fws->signDocument(saveFilename, certNickname, password, reason, location, ownerPassword, userPassword); // Now remove the signature stuff in case the user wants to continue editing stuff // So the document object is clean - const Object &vRefObj = annotObj.dictLookupNF("V"); + const Object &vRefObj = sig->field->getObj()->dictLookupNF("V"); if (vRefObj.isRef()) { getXRef()->removeIndirectObject(vRefObj.getRef()); } - destPage->removeAnnot(signatureAnnot); - catalog->removeFormFromAcroForm(ref); - getXRef()->removeIndirectObject(ref); + destPage->removeAnnot(sig->annotWidget); + catalog->removeFormFromAcroForm(sig->ref); + getXRef()->removeIndirectObject(sig->ref); return res; } - return false; + return CryptoSign::SigningError::InternalError; } diff --git a/poppler/PDFDoc.h b/poppler/PDFDoc.h index 01592e0..cb68c7b 100644 --- a/poppler/PDFDoc.h +++ b/poppler/PDFDoc.h @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005, 2006, 2008 Brad Hards -// Copyright (C) 2005, 2009, 2014, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2005, 2009, 2014, 2015, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008 Carlos Garcia Campos @@ -32,13 +32,15 @@ // Copyright (C) 2016 Jakub Alba // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Evangelos Rigas -// Copyright (C) 2020-2022 Oliver Sander +// Copyright (C) 2020-2023 Oliver Sander // Copyright (C) 2020 Nelson Benítez León // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright (C) 2021 Marek Kasik // Copyright (C) 2022 Felix Jung // Copyright (C) 2022 crt +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -52,6 +54,7 @@ #include #include +#include "CryptoSignBackend.h" #include "poppler-config.h" #include "poppler_private_export.h" @@ -130,14 +133,13 @@ enum PDFSubtypeConformance class POPPLER_PRIVATE_EXPORT PDFDoc { public: - explicit PDFDoc(std::unique_ptr &&fileNameA, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr, - const std::function &xrefReconstructedCallback = {}); + explicit PDFDoc(std::unique_ptr &&fileNameA, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const std::function &xrefReconstructedCallback = {}); #ifdef _WIN32 - PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr, const std::function &xrefReconstructedCallback = {}); + PDFDoc(wchar_t *fileNameA, int fileNameLen, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const std::function &xrefReconstructedCallback = {}); #endif - explicit PDFDoc(BaseStream *strA, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr, const std::function &xrefReconstructedCallback = {}); + explicit PDFDoc(BaseStream *strA, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, const std::function &xrefReconstructedCallback = {}); ~PDFDoc(); PDFDoc(const PDFDoc &) = delete; @@ -146,34 +148,19 @@ class POPPLER_PRIVATE_EXPORT PDFDoc static std::unique_ptr ErrorPDFDoc(int errorCode, std::unique_ptr &&fileNameA); // Was PDF document successfully opened? - bool isOk() const - { - return ok; - } + bool isOk() const { return ok; } // Get the error code (if isOk() returns false). - int getErrorCode() const - { - return errCode; - } + int getErrorCode() const { return errCode; } // Get the error code returned by fopen() (if getErrorCode() == // errOpenFile). - int getFopenErrno() const - { - return fopenErrno; - } + int getFopenErrno() const { return fopenErrno; } // Get file name. - const GooString *getFileName() const - { - return fileName.get(); - } + const GooString *getFileName() const { return fileName.get(); } #ifdef _WIN32 - wchar_t *getFileNameU() - { - return fileNameU; - } + std::wstring getFileNameU() { return fileNameU; } #endif // Get the linearization table. @@ -181,68 +168,35 @@ class POPPLER_PRIVATE_EXPORT PDFDoc bool checkLinearization(); // Get the xref table. - XRef *getXRef() const - { - return xref; - } + XRef *getXRef() const { return xref; } // Get catalog. - Catalog *getCatalog() const - { - return catalog; - } + Catalog *getCatalog() const { return catalog; } // Get optional content configuration - OCGs *getOptContentConfig() const - { - return catalog->getOptContentConfig(); - } + const OCGs *getOptContentConfig() const { return catalog->getOptContentConfig(); } // Get base stream. - BaseStream *getBaseStream() const - { - return str; - } + BaseStream *getBaseStream() const { return str; } // Get page parameters. - double getPageMediaWidth(int page) - { - return getPage(page) ? getPage(page)->getMediaWidth() : 0.0; - } - double getPageMediaHeight(int page) - { - return getPage(page) ? getPage(page)->getMediaHeight() : 0.0; - } - double getPageCropWidth(int page) - { - return getPage(page) ? getPage(page)->getCropWidth() : 0.0; - } - double getPageCropHeight(int page) - { - return getPage(page) ? getPage(page)->getCropHeight() : 0.0; - } - int getPageRotate(int page) - { - return getPage(page) ? getPage(page)->getRotate() : 0; - } + double getPageMediaWidth(int page) { return getPage(page) ? getPage(page)->getMediaWidth() : 0.0; } + double getPageMediaHeight(int page) { return getPage(page) ? getPage(page)->getMediaHeight() : 0.0; } + double getPageCropWidth(int page) { return getPage(page) ? getPage(page)->getCropWidth() : 0.0; } + double getPageCropHeight(int page) { return getPage(page) ? getPage(page)->getCropHeight() : 0.0; } + int getPageRotate(int page) { return getPage(page) ? getPage(page)->getRotate() : 0; } // Get number of pages. int getNumPages(); // Return the contents of the metadata stream, or nullptr if there is // no metadata. - std::unique_ptr readMetadata() const - { - return catalog->readMetadata(); - } + std::unique_ptr readMetadata() const { return catalog->readMetadata(); } // Return the structure tree root object. - const StructTreeRoot *getStructTreeRoot() const - { - return catalog->getStructTreeRoot(); - } + const StructTreeRoot *getStructTreeRoot() const { return catalog->getStructTreeRoot(); } - // Get page. + // Get page. First page is page 1. Page *getPage(int page); // Display a page. @@ -259,10 +213,7 @@ class POPPLER_PRIVATE_EXPORT PDFDoc // Find a page, given its object ID. Returns page number, or 0 if // not found. - int findPage(const Ref ref) - { - return catalog->findPage(ref); - } + int findPage(const Ref ref) { return catalog->findPage(ref); } // Returns the links for the current page, transferring ownership to // the caller. @@ -270,10 +221,7 @@ class POPPLER_PRIVATE_EXPORT PDFDoc // Find a named destination. Returns the link destination, or // nullptr if is not a destination. - std::unique_ptr findDest(const GooString *name) - { - return catalog->findDest(name); - } + std::unique_ptr findDest(const GooString *name) { return catalog->findDest(name); } // Process the links for a page. void processLinks(OutputDev *out, int page); @@ -282,162 +230,65 @@ class POPPLER_PRIVATE_EXPORT PDFDoc Outline *getOutline(); // Is the file encrypted? - bool isEncrypted() - { - return xref->isEncrypted(); - } + bool isEncrypted() { return xref->isEncrypted(); } std::vector getSignatureFields(); // Check various permissions. - bool okToPrint(bool ignoreOwnerPW = false) - { - return xref->okToPrint(ignoreOwnerPW); - } - bool okToPrintHighRes(bool ignoreOwnerPW = false) - { - return xref->okToPrintHighRes(ignoreOwnerPW); - } - bool okToChange(bool ignoreOwnerPW = false) - { - return xref->okToChange(ignoreOwnerPW); - } - bool okToCopy(bool ignoreOwnerPW = false) - { - return xref->okToCopy(ignoreOwnerPW); - } - bool okToAddNotes(bool ignoreOwnerPW = false) - { - return xref->okToAddNotes(ignoreOwnerPW); - } - bool okToFillForm(bool ignoreOwnerPW = false) - { - return xref->okToFillForm(ignoreOwnerPW); - } - bool okToAccessibility(bool ignoreOwnerPW = false) - { - return xref->okToAccessibility(ignoreOwnerPW); - } - bool okToAssemble(bool ignoreOwnerPW = false) - { - return xref->okToAssemble(ignoreOwnerPW); - } + bool okToPrint(bool ignoreOwnerPW = false) { return xref->okToPrint(ignoreOwnerPW); } + bool okToPrintHighRes(bool ignoreOwnerPW = false) { return xref->okToPrintHighRes(ignoreOwnerPW); } + bool okToChange(bool ignoreOwnerPW = false) { return xref->okToChange(ignoreOwnerPW); } + bool okToCopy(bool ignoreOwnerPW = false) { return xref->okToCopy(ignoreOwnerPW); } + bool okToAddNotes(bool ignoreOwnerPW = false) { return xref->okToAddNotes(ignoreOwnerPW); } + bool okToFillForm(bool ignoreOwnerPW = false) { return xref->okToFillForm(ignoreOwnerPW); } + bool okToAccessibility(bool ignoreOwnerPW = false) { return xref->okToAccessibility(ignoreOwnerPW); } + bool okToAssemble(bool ignoreOwnerPW = false) { return xref->okToAssemble(ignoreOwnerPW); } // Is this document linearized? bool isLinearized(bool tryingToReconstruct = false); // Return the document's Info dictionary (if any). - Object getDocInfo() - { - return xref->getDocInfo(); - } - Object getDocInfoNF() - { - return xref->getDocInfoNF(); - } + Object getDocInfo() { return xref->getDocInfo(); } + Object getDocInfoNF() { return xref->getDocInfoNF(); } // Remove the document's Info dictionary and update the trailer dictionary. - void removeDocInfo() - { - xref->removeDocInfo(); - } + void removeDocInfo() { xref->removeDocInfo(); } // Set doc info string entry. nullptr or empty value will cause a removal. // Takes ownership of value. - void setDocInfoStringEntry(const char *key, GooString *value); + void setDocInfoStringEntry(const char *key, std::unique_ptr value); // Set document's properties in document's Info dictionary. // nullptr or empty value will cause a removal. - // Takes ownership of value. - void setDocInfoTitle(GooString *title) - { - setDocInfoStringEntry("Title", title); - } - void setDocInfoAuthor(GooString *author) - { - setDocInfoStringEntry("Author", author); - } - void setDocInfoSubject(GooString *subject) - { - setDocInfoStringEntry("Subject", subject); - } - void setDocInfoKeywords(GooString *keywords) - { - setDocInfoStringEntry("Keywords", keywords); - } - void setDocInfoCreator(GooString *creator) - { - setDocInfoStringEntry("Creator", creator); - } - void setDocInfoProducer(GooString *producer) - { - setDocInfoStringEntry("Producer", producer); - } - void setDocInfoCreatDate(GooString *creatDate) - { - setDocInfoStringEntry("CreationDate", creatDate); - } - void setDocInfoModDate(GooString *modDate) - { - setDocInfoStringEntry("ModDate", modDate); - } + void setDocInfoTitle(std::unique_ptr title) { setDocInfoStringEntry("Title", std::move(title)); } + void setDocInfoAuthor(std::unique_ptr author) { setDocInfoStringEntry("Author", std::move(author)); } + void setDocInfoSubject(std::unique_ptr subject) { setDocInfoStringEntry("Subject", std::move(subject)); } + void setDocInfoKeywords(std::unique_ptr keywords) { setDocInfoStringEntry("Keywords", std::move(keywords)); } + void setDocInfoCreator(std::unique_ptr creator) { setDocInfoStringEntry("Creator", std::move(creator)); } + void setDocInfoProducer(std::unique_ptr producer) { setDocInfoStringEntry("Producer", std::move(producer)); } + void setDocInfoCreatDate(std::unique_ptr creatDate) { setDocInfoStringEntry("CreationDate", std::move(creatDate)); } + void setDocInfoModDate(std::unique_ptr modDate) { setDocInfoStringEntry("ModDate", std::move(modDate)); } // Get document's properties from document's Info dictionary. // Returns nullptr on fail. std::unique_ptr getDocInfoStringEntry(const char *key); - std::unique_ptr getDocInfoTitle() - { - return getDocInfoStringEntry("Title"); - } - std::unique_ptr getDocInfoAuthor() - { - return getDocInfoStringEntry("Author"); - } - std::unique_ptr getDocInfoSubject() - { - return getDocInfoStringEntry("Subject"); - } - std::unique_ptr getDocInfoKeywords() - { - return getDocInfoStringEntry("Keywords"); - } - std::unique_ptr getDocInfoCreator() - { - return getDocInfoStringEntry("Creator"); - } - std::unique_ptr getDocInfoProducer() - { - return getDocInfoStringEntry("Producer"); - } - std::unique_ptr getDocInfoCreatDate() - { - return getDocInfoStringEntry("CreationDate"); - } - std::unique_ptr getDocInfoModDate() - { - return getDocInfoStringEntry("ModDate"); - } + std::unique_ptr getDocInfoTitle() { return getDocInfoStringEntry("Title"); } + std::unique_ptr getDocInfoAuthor() { return getDocInfoStringEntry("Author"); } + std::unique_ptr getDocInfoSubject() { return getDocInfoStringEntry("Subject"); } + std::unique_ptr getDocInfoKeywords() { return getDocInfoStringEntry("Keywords"); } + std::unique_ptr getDocInfoCreator() { return getDocInfoStringEntry("Creator"); } + std::unique_ptr getDocInfoProducer() { return getDocInfoStringEntry("Producer"); } + std::unique_ptr getDocInfoCreatDate() { return getDocInfoStringEntry("CreationDate"); } + std::unique_ptr getDocInfoModDate() { return getDocInfoStringEntry("ModDate"); } // Return the PDF subtype, part, and conformance - PDFSubtype getPDFSubtype() const - { - return pdfSubtype; - } - PDFSubtypePart getPDFSubtypePart() const - { - return pdfPart; - } - PDFSubtypeConformance getPDFSubtypeConformance() const - { - return pdfConformance; - } + PDFSubtype getPDFSubtype() const { return pdfSubtype; } + PDFSubtypePart getPDFSubtypePart() const { return pdfPart; } + PDFSubtypeConformance getPDFSubtypeConformance() const { return pdfConformance; } // Return the PDF version specified by the file (either header or catalog). - int getPDFMajorVersion() const - { - return std::max(headerPdfMajorVersion, catalog->getPDFMajorVersion()); - } + int getPDFMajorVersion() const { return std::max(headerPdfMajorVersion, catalog->getPDFMajorVersion()); } int getPDFMinorVersion() const { const int catalogMajorVersion = catalog->getPDFMajorVersion(); @@ -464,12 +315,6 @@ class POPPLER_PRIVATE_EXPORT PDFDoc // Save this file in the given output stream without saving changes int saveWithoutChangesAs(OutStream *outStr); - // Return a pointer to the GUI (XPDFCore or WinPDFCore object). - void *getGUIData() - { - return guiData; - } - // rewrite pageDict with MediaBox, CropBox and new page CTM bool replacePageDict(int pageNo, int rotate, const PDFRectangle *mediaBox, const PDFRectangle *cropBox); bool markPageObjects(Dict *pageDict, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts = nullptr); @@ -491,14 +336,32 @@ class POPPLER_PRIVATE_EXPORT PDFDoc // Arguments reason and location are UTF-16 big endian strings with BOM. An empty string and nullptr are acceptable too. // Argument imagePath is a background image (a path to a file). // sign() takes ownership of partialFieldName. - bool sign(const char *saveFilename, const char *certNickname, const char *password, GooString *partialFieldName, int page, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, - double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, const GooString *reason = nullptr, - const GooString *location = nullptr, const std::string &imagePath = "", const std::optional &ownerPassword = {}, const std::optional &userPassword = {}); + std::optional sign(const std::string &saveFilename, const std::string &certNickname, const std::string &password, std::unique_ptr &&partialFieldName, int page, const PDFRectangle &rect, + const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, + std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, const GooString *reason = nullptr, const GooString *location = nullptr, + const std::string &imagePath = "", const std::optional &ownerPassword = {}, const std::optional &userPassword = {}); + + struct SignatureData + { + Ref ref; + AnnotWidget *annotWidget = nullptr; + FormWidget *formWidget = nullptr; + std::unique_ptr<::FormFieldSignature> field = nullptr; + }; + + std::optional createSignature(::Page *destPage, std::unique_ptr &&partialFieldName, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, + double leftFontSize, std::unique_ptr &&fontColor, double borderWidth, std::unique_ptr &&borderColor, std::unique_ptr &&backgroundColor, + const std::string &imagePath); private: // insert referenced objects in XRef bool markDictionary(Dict *dict, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts); bool markObject(Object *obj, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set *alreadyMarkedDicts = nullptr); + + // Sanitizes the string so that it does + // not contain any ( ) < > [ ] { } / % + static std::string sanitizedName(const std::string &name); + static void writeDictionary(Dict *dict, OutStream *outStr, XRef *xRef, unsigned int numOffset, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref, std::set *alreadyWrittenDicts); // Write object header to current file stream and return its offset @@ -509,10 +372,7 @@ class POPPLER_PRIVATE_EXPORT PDFDoc { writeObject(obj, outStr, getXRef(), 0, fileKey, encAlgorithm, keyLength, { objNum, objGen }); } - inline void writeObject(Object *obj, OutStream *outStr, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref) - { - writeObject(obj, outStr, getXRef(), 0, fileKey, encAlgorithm, keyLength, ref); - } + inline void writeObject(Object *obj, OutStream *outStr, unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, Ref ref) { writeObject(obj, outStr, getXRef(), 0, fileKey, encAlgorithm, keyLength, ref); } static void writeStream(Stream *str, OutStream *outStr); static void writeRawStream(Stream *str, OutStream *outStr); void writeXRefTableTrailer(Goffset uxrefOffset, XRef *uxref, bool writeAllEntries, int uxrefSize, OutStream *outStr, bool incrUpdate); @@ -520,7 +380,7 @@ class POPPLER_PRIVATE_EXPORT PDFDoc void saveIncrementalUpdate(OutStream *outStr); void saveCompleteRewrite(OutStream *outStr); - Page *parsePage(int page); + std::unique_ptr parsePage(int page); // Get hints. Hints *getHints(); @@ -541,11 +401,10 @@ class POPPLER_PRIVATE_EXPORT PDFDoc std::unique_ptr fileName; #ifdef _WIN32 - wchar_t *fileNameU = nullptr; + std::wstring fileNameU; #endif std::unique_ptr file; BaseStream *str = nullptr; - void *guiData = nullptr; int headerPdfMajorVersion; int headerPdfMinorVersion; PDFSubtype pdfSubtype; @@ -561,7 +420,7 @@ class POPPLER_PRIVATE_EXPORT PDFDoc Catalog *catalog = nullptr; Hints *hints = nullptr; Outline *outline = nullptr; - Page **pageCache = nullptr; + std::vector> pageCache; bool ok = false; int errCode = errNone; diff --git a/poppler/PDFDocBuilder.h b/poppler/PDFDocBuilder.h index d0bd1ea..43988e3 100644 --- a/poppler/PDFDocBuilder.h +++ b/poppler/PDFDocBuilder.h @@ -7,6 +7,7 @@ // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2020, 2022 Albert Astals Cid // Copyright 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -37,8 +38,7 @@ class PDFDocBuilder // Builds a new PDFDoc. Returns a PDFDoc. You should check this PDFDoc // with PDFDoc::isOk() for failures. - // The caller is responsible for deleting ownerPassword, userPassWord and guiData. - virtual std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr) = 0; + virtual std::unique_ptr buildPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}) = 0; // Returns true if the builder supports building a PDFDoc from the URI. virtual bool supports(const GooString &uri) = 0; diff --git a/poppler/PDFDocEncoding.h b/poppler/PDFDocEncoding.h index 01f3815..6cc0ebf 100644 --- a/poppler/PDFDocEncoding.h +++ b/poppler/PDFDocEncoding.h @@ -16,6 +16,7 @@ // Copyright (C) 2007 Adrian Johnson // Copyright (C) 2019 Volker Krause // Copyright (C) 2020 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -34,6 +35,6 @@ class GooString; extern const Unicode POPPLER_PRIVATE_EXPORT pdfDocEncoding[256]; -char POPPLER_PRIVATE_EXPORT *pdfDocEncodingToUTF16(const std::string &orig, int *length); +std::string POPPLER_PRIVATE_EXPORT pdfDocEncodingToUTF16(const std::string &orig); #endif diff --git a/poppler/PDFDocFactory.cc b/poppler/PDFDocFactory.cc index e4db3e4..1acdde3 100644 --- a/poppler/PDFDocFactory.cc +++ b/poppler/PDFDocFactory.cc @@ -5,11 +5,12 @@ // This file is licensed under the GPLv2 or later // // Copyright 2010 Hib Eris -// Copyright 2010, 2022 Albert Astals Cid +// Copyright 2010, 2022, 2024 Albert Astals Cid // Copyright 2017 Adrian Johnson // Copyright 2018 Adam Reichold // Copyright 2019, 2021 Oliver Sander // Copyright 2021 Christian Persch +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -54,17 +55,17 @@ PDFDocFactory::~PDFDocFactory() } } -std::unique_ptr PDFDocFactory::createPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword, void *guiDataA) +std::unique_ptr PDFDocFactory::createPDFDoc(const GooString &uri, const std::optional &ownerPassword, const std::optional &userPassword) { for (int i = builders->size() - 1; i >= 0; i--) { PDFDocBuilder *builder = (*builders)[i]; if (builder->supports(uri)) { - return builder->buildPDFDoc(uri, ownerPassword, userPassword, guiDataA); + return builder->buildPDFDoc(uri, ownerPassword, userPassword); } } error(errInternal, -1, "Cannot handle URI '{0:t}'.", &uri); - return PDFDoc::ErrorPDFDoc(errOpenFile, std::unique_ptr(uri.copy())); + return PDFDoc::ErrorPDFDoc(errOpenFile, uri.copy()); } void PDFDocFactory::registerPDFDocBuilder(PDFDocBuilder *pdfDocBuilder) diff --git a/poppler/PDFDocFactory.h b/poppler/PDFDocFactory.h index e84aca4..9e70b01 100644 --- a/poppler/PDFDocFactory.h +++ b/poppler/PDFDocFactory.h @@ -7,6 +7,7 @@ // Copyright 2010 Hib Eris // Copyright 2010, 2018, 2021, 2022 Albert Astals Cid // Copyright 2019, 2021 Oliver Sander +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -45,8 +46,7 @@ class POPPLER_PRIVATE_EXPORT PDFDocFactory // Create a PDFDoc. Returns a PDFDoc. You should check this PDFDoc // with PDFDoc::isOk() for failures. - // The caller is responsible for deleting ownerPassword, userPassWord and guiData. - std::unique_ptr createPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}, void *guiDataA = nullptr); + std::unique_ptr createPDFDoc(const GooString &uri, const std::optional &ownerPassword = {}, const std::optional &userPassword = {}); // Extend supported URIs with the ones from the PDFDocBuilder. void registerPDFDocBuilder(PDFDocBuilder *pdfDocBuilder); diff --git a/poppler/PSOutputDev.cc b/poppler/PSOutputDev.cc index cdabc25..3de0d63 100644 --- a/poppler/PSOutputDev.cc +++ b/poppler/PSOutputDev.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Martin Kretzschmar // Copyright (C) 2005, 2006 Kristian Høgsberg -// Copyright (C) 2006-2009, 2011-2013, 2015-2022 Albert Astals Cid +// Copyright (C) 2006-2009, 2011-2013, 2015-2022, 2024, 2025 Albert Astals Cid // Copyright (C) 2006 Jeff Muizelaar // Copyright (C) 2007, 2008 Brad Hards // Copyright (C) 2008, 2009 Koji Otani @@ -35,9 +35,11 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018 Philipp Knechtges // Copyright (C) 2019, 2021 Christian Persch -// Copyright (C) 2019, 2021, 2022 Oliver Sander +// Copyright (C) 2019, 2021-2024 Oliver Sander // Copyright (C) 2020, 2021 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere +// Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024, 2025 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -69,9 +71,7 @@ #include "Catalog.h" #include "Page.h" #include "Stream.h" -#ifdef ENABLE_ZLIB -# include "FlateEncoder.h" -#endif +#include "FlateEncoder.h" #ifdef ENABLE_ZLIB_UNCOMPRESS # include "FlateStream.h" #endif @@ -85,6 +85,7 @@ #include "SplashOutputDev.h" #include "PSOutputDev.h" #include "PDFDoc.h" +#include "UTF.h" #ifdef USE_CMS # include @@ -904,15 +905,21 @@ static const PSSubstFont psBase14SubstFonts[14] = { { "Courier", 0.600 }, struct PST1FontName { Ref fontFileID; - GooString *psName; // PostScript font name used for this - // embedded font file + std::unique_ptr psName; // PostScript font name used for this + // embedded font file + + // TODO Remove when we decide that not compiling with clang 15 (macos 14) is acceptable + PST1FontName(Ref id, std::unique_ptr &&name) : fontFileID(id), psName(std::move(name)) { } }; // Info for 8-bit fonts struct PSFont8Info { Ref fontID; - int *codeToGID; // code-to-GID mapping for TrueType fonts + std::vector codeToGID; // code-to-GID mapping for TrueType fonts + + // TODO Remove when we decide that not compiling with clang 15 (macos 14) is acceptable + PSFont8Info(Ref id, std::vector &&ctg) : fontID(id), codeToGID(std::move(ctg)) { } }; // Encoding info for substitute 16-bit font @@ -939,31 +946,27 @@ struct PSFont16Enc class PSOutCustomColor { public: - PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA); + PSOutCustomColor(double cA, double mA, double yA, double kA, std::unique_ptr &&nameA); ~PSOutCustomColor(); PSOutCustomColor(const PSOutCustomColor &) = delete; PSOutCustomColor &operator=(const PSOutCustomColor &) = delete; double c, m, y, k; - GooString *name; + const std::unique_ptr name; PSOutCustomColor *next; }; -PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, GooString *nameA) +PSOutCustomColor::PSOutCustomColor(double cA, double mA, double yA, double kA, std::unique_ptr &&nameA) : name(std::move(nameA)) { c = cA; m = mA; y = yA; k = kA; - name = nameA; next = nullptr; } -PSOutCustomColor::~PSOutCustomColor() -{ - delete name; -} +PSOutCustomColor::~PSOutCustomColor() = default; //------------------------------------------------------------------------ @@ -972,18 +975,6 @@ struct PSOutImgClipRect int x0, x1, y0, y1; }; -//------------------------------------------------------------------------ - -struct PSOutPaperSize -{ - PSOutPaperSize(std::unique_ptr &&nameA, int wA, int hA) : name(std::move(nameA)), w(wA), h(hA) { } - ~PSOutPaperSize() = default; - PSOutPaperSize(const PSOutPaperSize &) = delete; - PSOutPaperSize &operator=(const PSOutPaperSize &) = delete; - std::unique_ptr name; - int w, h; -}; - //------------------------------------------------------------------------ // DeviceNRecoder //------------------------------------------------------------------------ @@ -994,10 +985,10 @@ class DeviceNRecoder : public FilterStream DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageColorMap *colorMapA); ~DeviceNRecoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + bool reset() override; int getChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx++]; } int lookChar() override { return (bufIdx >= bufSize && !fillBuf()) ? EOF : buf[bufIdx]; } - GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } + std::optional getPSFilter(int psLevel, const char *indent) override { return {}; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } @@ -1028,18 +1019,16 @@ DeviceNRecoder::DeviceNRecoder(Stream *strA, int widthA, int heightA, GfxImageCo DeviceNRecoder::~DeviceNRecoder() { - if (imgStr) { - delete imgStr; - } + delete imgStr; if (str->isEncoder()) { delete str; } } -void DeviceNRecoder::reset() +bool DeviceNRecoder::reset() { imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + return imgStr->reset(); } bool DeviceNRecoder::fillBuf() @@ -1092,13 +1081,9 @@ PSOutputDev::PSOutputDev(const char *fileName, PDFDoc *docA, char *psTitleA, con customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; - fontIDs = nullptr; - t1FontNames = nullptr; - font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; - paperSizes = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; @@ -1151,13 +1136,9 @@ PSOutputDev::PSOutputDev(int fdA, PDFDoc *docA, char *psTitleA, const std::vecto customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; - fontIDs = nullptr; - t1FontNames = nullptr; - font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; - paperSizes = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; @@ -1191,13 +1172,9 @@ PSOutputDev::PSOutputDev(FoFiOutputFunc outputFuncA, void *outputStreamA, char * customCodeCbk = customCodeCbkA; customCodeCbkData = customCodeCbkDataA; - fontIDs = nullptr; - t1FontNames = nullptr; - font8Info = nullptr; font16Enc = nullptr; imgIDs = nullptr; formIDs = nullptr; - paperSizes = nullptr; embFontList = nullptr; customColors = nullptr; haveTextClip = false; @@ -1313,7 +1290,6 @@ void PSOutputDev::postInit() { Catalog *catalog; PDFRectangle *box; - PSOutPaperSize *size; int w, h, i; if (postInitDone || !ok) { @@ -1331,7 +1307,7 @@ void PSOutputDev::postInit() paperMatch = false; } - paperSizes = new std::vector(); + paperSizes.clear(); for (const int pg : pages) { Page *page = catalog->getPage(pg); if (page == nullptr) { @@ -1366,28 +1342,28 @@ void PSOutputDev::postInit() if (h > paperHeight) { paperHeight = h; } - for (i = 0; i < (int)paperSizes->size(); ++i) { - size = (*paperSizes)[i]; - if (pageDimensionEqual(w, size->w) && pageDimensionEqual(h, size->h)) { + for (i = 0; i < (int)paperSizes.size(); ++i) { + const PSOutPaperSize &size = paperSizes[i]; + if (pageDimensionEqual(w, size.w) && pageDimensionEqual(h, size.h)) { break; } } - if (i == (int)paperSizes->size()) { + if (i == (int)paperSizes.size()) { const StandardMedia *media = standardMedia; - std::unique_ptr name; + std::string name; while (media->name) { if (pageDimensionEqual(w, media->width) && pageDimensionEqual(h, media->height)) { - name = std::make_unique(media->name); + name = std::string(media->name); w = media->width; h = media->height; break; } media++; } - if (!name) { + if (name.empty()) { name = GooString::format("{0:d}x{1:d}mm", int(w * 25.4 / 72), int(h * 25.4 / 72)); } - paperSizes->push_back(new PSOutPaperSize(std::move(name), w, h)); + paperSizes.emplace_back(std::move(name), w, h); } pagePaperSize.insert(std::pair(pg, i)); if (!paperMatch) { @@ -1407,17 +1383,11 @@ void PSOutputDev::postInit() } // initialize fontIDs, fontFileIDs, and fontFileNames lists - fontIDSize = 64; - fontIDLen = 0; - fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref)); + fontIDs.reserve(64); + fontIDs.resize(0); for (i = 0; i < 14; ++i) { fontNames.emplace(psBase14SubstFonts[i].psName); } - t1FontNameSize = 64; - t1FontNameLen = 0; - t1FontNames = (PST1FontName *)gmallocn(t1FontNameSize, sizeof(PST1FontName)); - font8InfoLen = 0; - font8InfoSize = 0; font16EncLen = 0; font16EncSize = 0; imgIDLen = 0; @@ -1541,35 +1511,10 @@ PSOutputDev::~PSOutputDev() } #endif } - if (paperSizes) { - for (auto entry : *paperSizes) { - delete entry; - } - delete paperSizes; - } - if (embFontList) { - delete embFontList; - } - if (fontIDs) { - gfree(fontIDs); - } - if (t1FontNames) { - for (i = 0; i < t1FontNameLen; ++i) { - delete t1FontNames[i].psName; - } - gfree(t1FontNames); - } - if (font8Info) { - for (i = 0; i < font8InfoLen; ++i) { - gfree(font8Info[i].codeToGID); - } - gfree(font8Info); - } + delete embFontList; if (font16Enc) { for (i = 0; i < font16EncLen; ++i) { - if (font16Enc[i].enc) { - delete font16Enc[i].enc; - } + delete font16Enc[i].enc; } gfree(font16Enc); } @@ -1586,7 +1531,6 @@ PSOutputDev::~PSOutputDev() void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PDFRectangle *cropBox, int pageRotate, const char *title) { - PSOutPaperSize *size; double x1, y1, x2, y2; switch (mode) { @@ -1600,15 +1544,24 @@ void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PD writePS("%!PS-Adobe-3.0 Resource-Form\n"); break; } - writePSFmt("%Produced by poppler pdftops version: {0:s} (http://poppler.freedesktop.org)\n", PACKAGE_VERSION); Object info = xref->getDocInfo(); + std::string creator = GooString::format("poppler pdftops version: {0:s} (http://poppler.freedesktop.org)", PACKAGE_VERSION); if (info.isDict()) { Object obj1 = info.dictLookup("Creator"); if (obj1.isString()) { - writePS("%%Creator: "); - writePSTextLine(obj1.getString()); + const GooString *pdfCreator = obj1.getString(); + if (pdfCreator && !pdfCreator->toStr().empty()) { + creator.append(". PDF Creator: "); + if (hasUnicodeByteOrderMark(pdfCreator->toStr())) { + creator.append(TextStringToUtf8(pdfCreator->toStr())); + } else { + creator.append(pdfCreator->toStr()); + } + } } } + writePS("%%Creator: "); + writePSTextLine(creator); if (title) { char *sanitizedTitle = strdup(title); for (size_t i = 0; i < strlen(sanitizedTitle); ++i) { @@ -1631,17 +1584,16 @@ void PSOutputDev::writeHeader(int nPages, const PDFRectangle *mediaBox, const PD switch (mode) { case psModePS: - for (std::size_t i = 0; i < paperSizes->size(); ++i) { - size = (*paperSizes)[i]; - writePSFmt("%%{0:s} {1:t} {2:d} {3:d} 0 () ()\n", i == 0 ? "DocumentMedia:" : "+", size->name.get(), size->w, size->h); + for (std::size_t i = 0; i < paperSizes.size(); ++i) { + const PSOutPaperSize &size = paperSizes[i]; + writePSFmt("%%{0:s} {1:s} {2:d} {3:d} 0 () ()\n", i == 0 ? "DocumentMedia:" : "+", size.name.c_str(), size.w, size.h); } writePSFmt("%%BoundingBox: 0 0 {0:d} {1:d}\n", paperWidth, paperHeight); writePSFmt("%%Pages: {0:d}\n", nPages); writePS("%%EndComments\n"); if (!paperMatch) { - size = (*paperSizes)[0]; writePS("%%BeginDefaults\n"); - writePSFmt("%%PageMedia: {0:t}\n", size->name.get()); + writePSFmt("%%PageMedia: {0:s}\n", paperSizes[0].name.c_str()); writePS("%%EndDefaults\n"); } break; @@ -1926,34 +1878,21 @@ void PSOutputDev::setupResources(Dict *resDict) void PSOutputDev::setupFonts(Dict *resDict) { - Ref r; - GfxFontDict *gfxFontDict; - int i; - - gfxFontDict = nullptr; - const Object &obj1 = resDict->lookupNF("Font"); - if (obj1.isRef()) { - Object obj2 = obj1.fetch(xref); - if (obj2.isDict()) { - r = obj1.getRef(); - gfxFontDict = new GfxFontDict(xref, &r, obj2.getDict()); - } - } else if (obj1.isDict()) { - gfxFontDict = new GfxFontDict(xref, nullptr, obj1.getDict()); - } - if (gfxFontDict) { - for (i = 0; i < gfxFontDict->getNumFonts(); ++i) { - if (const std::shared_ptr &font = gfxFontDict->getFont(i)) { + Ref fontDictRef; + const Object &fontDictObj = resDict->lookup("Font", &fontDictRef); + if (fontDictObj.isDict()) { + GfxFontDict gfxFontDict(xref, fontDictRef, fontDictObj.getDict()); + for (int i = 0; i < gfxFontDict.getNumFonts(); ++i) { + if (const std::shared_ptr &font = gfxFontDict.getFont(i)) { setupFont(font.get(), resDict); } } - delete gfxFontDict; } } void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) { - GooString *psName; + std::unique_ptr psName; char buf[16]; bool subst; const char *charName; @@ -1963,26 +1902,20 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) int i, j; // check if font is already set up - for (i = 0; i < fontIDLen; ++i) { - if (fontIDs[i] == *font->getID()) { + for (Ref fontID : fontIDs) { + if (fontID == *font->getID()) { return; } } - // add entry to fontIDs list - if (fontIDLen >= fontIDSize) { - fontIDSize += 64; - fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref)); - } - fontIDs[fontIDLen++] = *font->getID(); + fontIDs.push_back(*font->getID()); - psName = nullptr; xs = ys = 1; subst = false; if (font->getType() == fontType3) { - psName = GooString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen).release(); - setupType3Font(font, psName, parentResDict); + psName = std::make_unique(GooString::format("T3_{0:d}_{1:d}", font->getID()->num, font->getID()->gen)); + setupType3Font(font, psName.get(), parentResDict); } else { std::optional fontLoc = font->locateFont(xref, this); if (fontLoc) { @@ -1991,35 +1924,35 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) switch (fontLoc->fontType) { case fontType1: // this assumes that the PS font name matches the PDF font name - psName = font->getEmbeddedFontName() ? font->getEmbeddedFontName()->copy() : new GooString(); - setupEmbeddedType1Font(&fontLoc->embFontID, psName); + psName = font->getEmbeddedFontName() ? font->getEmbeddedFontName()->copy() : std::make_unique(); + setupEmbeddedType1Font(&fontLoc->embFontID, psName.get()); break; case fontType1C: psName = makePSFontName(font, &fontLoc->embFontID); - setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName); + setupEmbeddedType1CFont(font, &fontLoc->embFontID, psName.get()); break; case fontType1COT: psName = makePSFontName(font, &fontLoc->embFontID); - setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName); + setupEmbeddedOpenTypeT1CFont(font, &fontLoc->embFontID, psName.get(), fontLoc->fontNum); break; case fontTrueType: case fontTrueTypeOT: psName = makePSFontName(font, font->getID()); - setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName); + setupEmbeddedTrueTypeFont(font, &fontLoc->embFontID, psName.get(), fontLoc->fontNum); break; case fontCIDType0C: psName = makePSFontName(font, &fontLoc->embFontID); - setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName); + setupEmbeddedCIDType0Font(font, &fontLoc->embFontID, psName.get()); break; case fontCIDType2: case fontCIDType2OT: psName = makePSFontName(font, font->getID()); //~ should check to see if font actually uses vertical mode - setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName, true); + setupEmbeddedCIDTrueTypeFont(font, &fontLoc->embFontID, psName.get(), true, fontLoc->fontNum); break; case fontCIDType0COT: psName = makePSFontName(font, &fontLoc->embFontID); - setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName); + setupEmbeddedOpenTypeCFFFont(font, &fontLoc->embFontID, psName.get(), fontLoc->fontNum); break; default: break; @@ -2036,25 +1969,25 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) //~ this won't work -- the PS font name won't match psName = makePSFontName(font, font->getID()); } - setupExternalType1Font(fontLoc->pathAsGooString(), psName); + setupExternalType1Font(fontLoc->path, psName.get()); break; case fontTrueType: case fontTrueTypeOT: psName = makePSFontName(font, font->getID()); - setupExternalTrueTypeFont(font, fontLoc->pathAsGooString(), psName); + setupExternalTrueTypeFont(font, fontLoc->path, psName.get(), fontLoc->fontNum); break; case fontCIDType2: case fontCIDType2OT: psName = makePSFontName(font, font->getID()); //~ should check to see if font actually uses vertical mode - setupExternalCIDTrueTypeFont(font, fontLoc->pathAsGooString(), psName, true); + setupExternalCIDTrueTypeFont(font, fontLoc->path, psName.get(), true, fontLoc->fontNum); break; default: break; } break; case gfxFontLocResident: - psName = new GooString(fontLoc->path); + psName = std::make_unique(fontLoc->path); break; } } @@ -2100,12 +2033,12 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) // generate PostScript code to set up the font if (font->isCIDFont()) { if (level == psLevel3 || level == psLevel3Sep) { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, psName, font->getWMode()); + writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n", font->getID()->num, font->getID()->gen, psName.get(), font->getWMode()); } else { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, psName, font->getWMode()); + writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n", font->getID()->num, font->getID()->gen, psName.get(), font->getWMode()); } } else { - writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, psName, xs, ys); + writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.6g} {4:.6g}\n", font->getID()->num, font->getID()->gen, psName.get(), xs, ys); for (i = 0; i < 256; i += 8) { writePS((char *)((i == 0) ? "[ " : " ")); for (j = 0; j < 8; ++j) { @@ -2128,8 +2061,6 @@ void PSOutputDev::setupFont(GfxFont *font, Dict *parentResDict) } writePS("pdfMakeFont\n"); } - - delete psName; } void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) @@ -2170,18 +2101,24 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) length2 = obj2.getInt(); length3 = obj3.getInt(); + if (!strObj.streamReset()) { + return; + } + // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); embFontList->append(psName->c_str()); embFontList->append("\n"); - strObj.streamReset(); if (strObj.streamGetChar() == 0x80 && strObj.streamGetChar() == 1) { // PFB format length1 = strObj.streamGetChar() | (strObj.streamGetChar() << 8) | (strObj.streamGetChar() << 16) | (strObj.streamGetChar() << 24); } else { - strObj.streamReset(); + if (!strObj.streamReset()) { + error(errSyntaxError, -1, "Failed reset stream"); + goto err1; + } } // copy ASCII portion of font for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) { @@ -2299,7 +2236,7 @@ void PSOutputDev::setupEmbeddedType1Font(Ref *id, GooString *psName) } } -void PSOutputDev::setupExternalType1Font(const GooString *fileName, GooString *psName) +void PSOutputDev::setupExternalType1Font(const std::string &fileName, GooString *psName) { static const char hexChar[17] = "0123456789abcdef"; FILE *fontFile; @@ -2316,7 +2253,7 @@ void PSOutputDev::setupExternalType1Font(const GooString *fileName, GooString *p embFontList->append("\n"); // copy the font file - if (!(fontFile = openFile(fileName->c_str(), "rb"))) { + if (!(fontFile = openFile(fileName.c_str(), "rb"))) { error(errIO, -1, "Couldn't open external font file"); return; } @@ -2375,23 +2312,16 @@ void PSOutputDev::setupExternalType1Font(const GooString *fileName, GooString *p void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, GooString *psName) { FoFiType1C *ffT1C; - int i; // check if font is already embedded - for (i = 0; i < t1FontNameLen; ++i) { - if (t1FontNames[i].fontFileID == *id) { + for (PST1FontName &it : t1FontNames) { + if (it.fontFileID == *id) { psName->clear(); - psName->insert(0, t1FontNames[i].psName); + psName->insert(0, it.psName.get()); return; } } - if (t1FontNameLen == t1FontNameSize) { - t1FontNameSize *= 2; - t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); - } - t1FontNames[t1FontNameLen].fontFileID = *id; - t1FontNames[t1FontNameLen].psName = psName->copy(); - ++t1FontNameLen; + t1FontNames.emplace_back(*id, psName->copy()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2412,25 +2342,17 @@ void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id, GooString *psN writePS("%%EndResource\n"); } -void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName) +void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex) { - int i; - // check if font is already embedded - for (i = 0; i < t1FontNameLen; ++i) { - if (t1FontNames[i].fontFileID == *id) { + for (PST1FontName &it : t1FontNames) { + if (it.fontFileID == *id) { psName->clear(); - psName->insert(0, t1FontNames[i].psName); + psName->insert(0, it.psName.get()); return; } } - if (t1FontNameLen == t1FontNameSize) { - t1FontNameSize *= 2; - t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); - } - t1FontNames[t1FontNameLen].fontFileID = *id; - t1FontNames[t1FontNameLen].psName = psName->copy(); - ++t1FontNameLen; + t1FontNames.emplace_back(*id, psName->copy()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2441,7 +2363,7 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString // convert it to a Type 1 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { - if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size())) { + if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size(), faceIndex)) { if (ffTT->isOpenTypeCFF()) { ffTT->convertToType1(psName->c_str(), nullptr, true, outputFunc, outputStream); } @@ -2452,10 +2374,8 @@ void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString writePS("%%EndResource\n"); } -void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName) +void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex) { - int *codeToGID; - // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); @@ -2465,17 +2385,11 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *p // convert it to a Type 42 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { - if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size())) { - codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT.get()); + if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size(), faceIndex)) { + std::vector codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT.get()); ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream); - if (codeToGID) { - if (font8InfoLen >= font8InfoSize) { - font8InfoSize += 16; - font8Info = (PSFont8Info *)greallocn(font8Info, font8InfoSize, sizeof(PSFont8Info)); - } - font8Info[font8InfoLen].fontID = *font->getID(); - font8Info[font8InfoLen].codeToGID = codeToGID; - ++font8InfoLen; + if (!codeToGID.empty()) { + font8Info.emplace_back(*font->getID(), std::move(codeToGID)); } } } @@ -2484,10 +2398,8 @@ void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *p writePS("%%EndResource\n"); } -void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName) +void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, const std::string &fileName, GooString *psName, int faceIndex) { - int *codeToGID; - // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); embFontList->append("%%+ font "); @@ -2495,17 +2407,11 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, const GooString *file embFontList->append("\n"); // convert it to a Type 42 font - if (std::unique_ptr ffTT = FoFiTrueType::load(fileName->c_str())) { - codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT.get()); + if (std::unique_ptr ffTT = FoFiTrueType::load(fileName.c_str(), faceIndex)) { + std::vector codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT.get()); ffTT->convertToType42(psName->c_str(), ((Gfx8BitFont *)font)->getHasEncoding() ? ((Gfx8BitFont *)font)->getEncoding() : nullptr, codeToGID, outputFunc, outputStream); - if (codeToGID) { - if (font8InfoLen >= font8InfoSize) { - font8InfoSize += 16; - font8Info = (PSFont8Info *)greallocn(font8Info, font8InfoSize, sizeof(PSFont8Info)); - } - font8Info[font8InfoLen].fontID = *font->getID(); - font8Info[font8InfoLen].codeToGID = codeToGID; - ++font8InfoLen; + if (!codeToGID.empty()) { + font8Info.emplace_back(*font->getID(), std::move(codeToGID)); } } @@ -2515,18 +2421,18 @@ void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, const GooString *file void PSOutputDev::updateFontMaxValidGlyph(GfxFont *font, int maxValidGlyph) { - if (maxValidGlyph >= 0 && font->getName()) { - auto &fontMaxValidGlyph = perFontMaxValidGlyph[*font->getName()]; + const std::optional &fontName = font->getName(); + if (maxValidGlyph >= 0 && fontName) { + auto &fontMaxValidGlyph = perFontMaxValidGlyph[*fontName]; if (fontMaxValidGlyph < maxValidGlyph) { fontMaxValidGlyph = maxValidGlyph; } } } -void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName, bool needVerticalMetrics) +void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, const std::string &fileName, GooString *psName, bool needVerticalMetrics, int faceIndex) { - int *codeToGID; - int codeToGIDLen; + std::vector codeToGID; // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2536,35 +2442,29 @@ void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, const GooString *f // convert it to a Type 0 font //~ this should use fontNum to load the correct font - if (std::unique_ptr ffTT = FoFiTrueType::load(fileName->c_str())) { + if (std::unique_ptr ffTT = FoFiTrueType::load(fileName.c_str(), faceIndex)) { // check for embedding permission if (ffTT->getEmbeddingRights() >= 1) { - codeToGID = nullptr; - codeToGIDLen = 0; - if (((GfxCIDFont *)font)->getCIDToGID()) { - codeToGIDLen = ((GfxCIDFont *)font)->getCIDToGIDLen(); - if (codeToGIDLen) { - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)font)->getCIDToGID(), codeToGIDLen * sizeof(int)); - } + if (((GfxCIDFont *)font)->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)font)->getCIDToGID(); } else { - codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT.get(), &codeToGIDLen); + codeToGID = ((GfxCIDFont *)font)->getCodeToGIDMap(ffTT.get()); } if (ffTT->isOpenTypeCFF()) { - ffTT->convertToCIDType0(psName->c_str(), codeToGID, codeToGIDLen, outputFunc, outputStream); + ffTT->convertToCIDType0(psName->c_str(), codeToGID, outputFunc, outputStream); } else if (level >= psLevel3) { // Level 3: use a CID font - ffTT->convertToCIDType2(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, outputFunc, outputStream); + ffTT->convertToCIDType2(psName->c_str(), codeToGID, needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font int maxValidGlyph = -1; - ffTT->convertToType0(psName->c_str(), codeToGID, codeToGIDLen, needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream); + ffTT->convertToType0(psName->c_str(), codeToGID, needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream); updateFontMaxValidGlyph(font, maxValidGlyph); } - gfree(codeToGID); } else { - error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", font->getName() ? font->getName()->c_str() : "(unnamed)"); + const std::optional &fontName = font->getName(); + error(errSyntaxError, -1, "TrueType font '{0:s}' does not allow embedding", fontName ? fontName->c_str() : "(unnamed)"); } } @@ -2575,23 +2475,16 @@ void PSOutputDev::setupExternalCIDTrueTypeFont(GfxFont *font, const GooString *f void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GooString *psName) { FoFiType1C *ffT1C; - int i; // check if font is already embedded - for (i = 0; i < t1FontNameLen; ++i) { - if (t1FontNames[i].fontFileID == *id) { + for (PST1FontName &it : t1FontNames) { + if (it.fontFileID == *id) { psName->clear(); - psName->insert(0, t1FontNames[i].psName); + psName->insert(0, it.psName.get()); return; } } - if (t1FontNameLen == t1FontNameSize) { - t1FontNameSize *= 2; - t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); - } - t1FontNames[t1FontNameLen].fontFileID = *id; - t1FontNames[t1FontNameLen].psName = psName->copy(); - ++t1FontNameLen; + t1FontNames.emplace_back(*id, psName->copy()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2605,10 +2498,10 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GooString *p if ((ffT1C = FoFiType1C::make(fontBuf->data(), fontBuf->size()))) { if (level >= psLevel3) { // Level 3: use a CID font - ffT1C->convertToCIDType0(psName->c_str(), nullptr, 0, outputFunc, outputStream); + ffT1C->convertToCIDType0(psName->c_str(), {}, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font - ffT1C->convertToType0(psName->c_str(), nullptr, 0, outputFunc, outputStream); + ffT1C->convertToType0(psName->c_str(), {}, outputFunc, outputStream); } delete ffT1C; } @@ -2618,7 +2511,7 @@ void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GooString *p writePS("%%EndResource\n"); } -void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics) +void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics, int faceIndex) { // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2629,14 +2522,14 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString // convert it to a Type 0 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { - if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size())) { + if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size(), faceIndex)) { if (level >= psLevel3) { // Level 3: use a CID font - ffTT->convertToCIDType2(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, outputFunc, outputStream); + ffTT->convertToCIDType2(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), needVerticalMetrics, outputFunc, outputStream); } else { // otherwise: use a non-CID composite font int maxValidGlyph = -1; - ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream); + ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), needVerticalMetrics, &maxValidGlyph, outputFunc, outputStream); updateFontMaxValidGlyph(font, maxValidGlyph); } } @@ -2646,25 +2539,17 @@ void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString writePS("%%EndResource\n"); } -void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName) +void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex) { - int i; - // check if font is already embedded - for (i = 0; i < t1FontNameLen; ++i) { - if (t1FontNames[i].fontFileID == *id) { + for (PST1FontName &it : t1FontNames) { + if (it.fontFileID == *id) { psName->clear(); - psName->insert(0, t1FontNames[i].psName); + psName->insert(0, it.psName.get()); return; } } - if (t1FontNameLen == t1FontNameSize) { - t1FontNameSize *= 2; - t1FontNames = (PST1FontName *)greallocn(t1FontNames, t1FontNameSize, sizeof(PST1FontName)); - } - t1FontNames[t1FontNameLen].fontFileID = *id; - t1FontNames[t1FontNameLen].psName = psName->copy(); - ++t1FontNameLen; + t1FontNames.emplace_back(*id, psName->copy()); // beginning comment writePSFmt("%%BeginResource: font {0:t}\n", psName); @@ -2675,14 +2560,14 @@ void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString // convert it to a Type 0 font const std::optional> fontBuf = font->readEmbFontFile(xref); if (fontBuf) { - if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size())) { + if (std::unique_ptr ffTT = FoFiTrueType::make(fontBuf->data(), fontBuf->size(), faceIndex)) { if (ffTT->isOpenTypeCFF()) { if (level >= psLevel3) { // Level 3: use a CID font - ffTT->convertToCIDType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); + ffTT->convertToCIDType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), outputFunc, outputStream); } else { // otherwise: use a non-CID composite font - ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), ((GfxCIDFont *)font)->getCIDToGIDLen(), outputFunc, outputStream); + ffTT->convertToType0(psName->c_str(), ((GfxCIDFont *)font)->getCIDToGID(), outputFunc, outputStream); } } } @@ -2753,13 +2638,13 @@ void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, Dict *parentR Object charProc = charProcs->getVal(i); gfx->display(&charProc); if (t3String) { - std::unique_ptr buf; + std::string buf; if (t3Cacheable) { buf = GooString::format("{0:.6g} {1:.6g} {2:.6g} {3:.6g} {4:.6g} {5:.6g} setcachedevice\n", t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY); } else { buf = GooString::format("{0:.6g} {1:.6g} setcharwidth\n", t3WX, t3WY); } - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); (*outputFunc)(outputStream, t3String->c_str(), t3String->getLength()); delete t3String; t3String = nullptr; @@ -2782,34 +2667,30 @@ void PSOutputDev::setupType3Font(GfxFont *font, GooString *psName, Dict *parentR // Make a unique PS font name, based on the names given in the PDF // font object, and an object ID (font file object for -GooString *PSOutputDev::makePSFontName(GfxFont *font, const Ref *id) +std::unique_ptr PSOutputDev::makePSFontName(GfxFont *font, const Ref *id) { - GooString *psName; const GooString *s; if ((s = font->getEmbeddedFontName())) { - psName = filterPSName(s->toStr()); - if (fontNames.emplace(psName->toStr()).second) { - return psName; + std::string psName = filterPSName(s->toStr()); + if (fontNames.emplace(psName).second) { + return std::make_unique(std::move(psName)); } - delete psName; } - if (font->getName()) { - psName = filterPSName(*font->getName()); - if (fontNames.emplace(psName->toStr()).second) { - return psName; + const std::optional &fontName = font->getName(); + if (fontName) { + std::string psName = filterPSName(*fontName); + if (fontNames.emplace(psName).second) { + return std::make_unique(std::move(psName)); } - delete psName; } - psName = GooString::format("FF{0:d}_{1:d}", id->num, id->gen).release(); + std::unique_ptr psName = std::make_unique(GooString::format("FF{0:d}_{1:d}", id->num, id->gen)); if ((s = font->getEmbeddedFontName())) { - s = filterPSName(s->toStr()); - psName->append('_')->append(s); - delete s; - } else if (font->getName()) { - s = filterPSName(*font->getName()); - psName->append('_')->append(s); - delete s; + std::string filteredName = filterPSName(s->toStr()); + psName->append('_')->append(filteredName); + } else if (fontName) { + std::string filteredName = filterPSName(*fontName); + psName->append('_')->append(filteredName); } fontNames.emplace(psName->toStr()); return psName; @@ -2870,7 +2751,6 @@ void PSOutputDev::setupImages(Dict *resDict) void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) { bool useFlate, useLZW, useRLE, useCompressed, doUseASCIIHex; - GooString *s; int c; int size, line, col, i; int outerSize, outer; @@ -2889,10 +2769,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) if (uncompressPreloadedImages) { /* nothing to do */; } else { - s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); + std::optional s = str->getPSFilter(level < psLevel3 ? 2 : 3, ""); if (s) { useCompressed = true; - delete s; } else { if (level >= psLevel3 && getEnableFlate()) { useFlate = true; @@ -2908,12 +2787,9 @@ void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) if (useCompressed) { str = str->getUndecodedStream(); } -#ifdef ENABLE_ZLIB if (useFlate) { str = new FlateEncoder(str); - } else -#endif - if (useLZW) { + } else if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); @@ -2925,7 +2801,10 @@ void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) } // compute image data size - str->reset(); + if (!str->reset()) { + delete str; + return; + } col = size = 0; do { do { @@ -2968,7 +2847,10 @@ void PSOutputDev::setupImage(Ref id, Stream *str, bool mask) str->close(); // write the data into the array - str->reset(); + if (!str->reset()) { + delete str; + return; + } for (outer = 0; outer < outerSize; outer++) { int innerSize = size > 65535 ? 65535 : size; @@ -3506,8 +3388,7 @@ bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, i } else { isOptimizedGray = false; } - str0->reset(); -#ifdef ENABLE_ZLIB + (void)str0->reset(); if (useFlate) { if (isOptimizedGray && numComps == 4) { str = new FlateEncoder(new CMYKGrayEncoder(str0)); @@ -3518,9 +3399,7 @@ bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, i } else { str = new FlateEncoder(str0); } - } else -#endif - if (useLZW) { + } else if (useLZW) { if (isOptimizedGray && numComps == 4) { str = new LZWEncoder(new CMYKGrayEncoder(str0)); numComps = 1; @@ -3589,14 +3468,14 @@ bool PSOutputDev::checkPageSlice(Page *page, double /*hDPI*/, double /*vDPI*/, i } else { str = new ASCII85Encoder(str); } - str->reset(); + (void)str->reset(); if (useBinary) { // Count the bytes to write a document comment int len = 0; while (str->getChar() != EOF) { len++; } - str->reset(); + (void)str->reset(); writePSFmt("%%BeginData: {0:d} Binary Bytes\n", len + 6 + 1); } writePS("image\n"); @@ -3631,7 +3510,6 @@ void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) int imgWidth, imgHeight, imgWidth2, imgHeight2; bool landscape; GooString *s; - PSOutPaperSize *paperSize; if (!postInitDone) { postInit(); @@ -3783,8 +3661,8 @@ void PSOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) ty += (rotate == 0 || rotate == 180) ? imgLLY : -imgLLX; if (paperMatch) { - paperSize = (*paperSizes)[pagePaperSize[pageNum]]; - writePSFmt("%%PageMedia: {0:t}\n", paperSize->name.get()); + const PSOutPaperSize &paperSize = paperSizes[pagePaperSize[pageNum]]; + writePSFmt("%%PageMedia: {0:s}\n", paperSize.name.c_str()); } // Create a matrix with the same transform that will be output to PS @@ -5016,7 +4894,7 @@ void PSOutputDev::drawString(GfxState *state, const GooString *s) { std::shared_ptr font; int wMode; - int *codeToGID; + std::vector codeToGID; GooString *s2; double dx, dy, originX, originY; const char *p; @@ -5048,7 +4926,8 @@ void PSOutputDev::drawString(GfxState *state, const GooString *s) if (!(font = state->getFont())) { return; } - maxGlyphInt = (font->getName() ? perFontMaxValidGlyph[*font->getName()] : 0); + const std::optional &fontName = font->getName(); + maxGlyphInt = (fontName ? perFontMaxValidGlyph[*fontName] : 0); if (maxGlyphInt < 0) { maxGlyphInt = 0; } @@ -5057,7 +4936,6 @@ void PSOutputDev::drawString(GfxState *state, const GooString *s) // check for a subtitute 16-bit font uMap = nullptr; - codeToGID = nullptr; if (font->isCIDFont()) { for (i = 0; i < font16EncLen; ++i) { if (*font->getID() == font16Enc[i].fontID) { @@ -5072,9 +4950,9 @@ void PSOutputDev::drawString(GfxState *state, const GooString *s) // check for a code-to-GID map } else { - for (i = 0; i < font8InfoLen; ++i) { - if (*font->getID() == font8Info[i].fontID) { - codeToGID = font8Info[i].codeToGID; + for (const auto &font8 : font8Info) { + if (*font->getID() == font8.fontID) { + codeToGID = font8.codeToGID; break; } } @@ -5143,7 +5021,7 @@ void PSOutputDev::drawString(GfxState *state, const GooString *s) ++nChars; } } else { - if (!codeToGID || codeToGID[code] >= 0) { + if (codeToGID.empty() || codeToGID[code] >= 0) { s2->append((char)code); dxdy[2 * nChars] = dx; dxdy[2 * nChars + 1] = dy; @@ -5270,7 +5148,6 @@ void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert) { - ImageStream *imgStr; unsigned char pixBuf[gfxColorMaxComps]; GfxGray gray; int col, x, y, c, i; @@ -5287,7 +5164,11 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert // create an array str = new FixedLengthEncoder(str, len); str = new ASCIIHexEncoder(str); - str->reset(); + if (!str->reset()) { + delete str; + return; + } + col = 0; writePS("[<"); do { @@ -5335,8 +5216,10 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert if (colorMap) { // set up to process the data stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr { str, width, colorMap->getNumPixelComps(), colorMap->getBits() }; + if (!imgStr.reset()) { + goto end; + } // process the data stream i = 0; @@ -5344,7 +5227,7 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert // write the line for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); + imgStr.getPixel(pixBuf); colorMap->getGray(pixBuf, &gray); grayValue = colToByte(gray); if (useBinary) { @@ -5371,11 +5254,11 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert writePSBuf(hexBuf, i); } str->close(); - delete imgStr; - // imagemask } else { - str->reset(); + if (!str->reset()) { + goto end; + } i = 0; for (y = 0; y < height; ++y) { for (x = 0; x < width; x += 8) { @@ -5406,6 +5289,7 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert str->close(); } } +end: if (maskStr && !(maskColors && colorMap)) { writePS("pdfImClipEnd\n"); @@ -5414,8 +5298,6 @@ void PSOutputDev::doImageL1(Object *ref, GfxImageColorMap *colorMap, bool invert void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool invert, bool inlineImg, Stream *str, int width, int height, int len, const int *maskColors, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert) { - ImageStream *imgStr; - unsigned char *lineBuf; unsigned char pixBuf[gfxColorMaxComps]; GfxCMYK cmyk; int x, y, i, comp; @@ -5429,18 +5311,16 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv maskToClippingPath(maskStr, maskWidth, maskHeight, maskInvert); } - // allocate a line buffer - lineBuf = (unsigned char *)gmallocn(width, 4); - // scan for all gray if (getOptimizeColorSpace()) { - ImageStream *imgCheckStr; - imgCheckStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgCheckStr->reset(); + ImageStream imgCheckStr { str, width, colorMap->getNumPixelComps(), colorMap->getBits() }; + if (!imgCheckStr.reset()) { + return; + } isGray = true; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { - imgCheckStr->getPixel(pixBuf); + imgCheckStr.getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); if (colToByte(cmyk.c) != colToByte(cmyk.m) || colToByte(cmyk.c) != colToByte(cmyk.y)) { isGray = false; @@ -5449,15 +5329,16 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } } } - imgCheckStr->close(); - delete imgCheckStr; + imgCheckStr.close(); } else { isGray = false; } // set up to process the data stream - imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr { str, width, colorMap->getNumPixelComps(), colorMap->getBits() }; + if (!imgStr.reset()) { + return; + } // width, height, matrix, bits per component writePSFmt("{0:d} {1:d} 8 [{2:d} 0 0 {3:d} 0 {4:d}] pdfIm1{5:s}{6:s}\n", width, height, width, -height, height, isGray ? "" : "Sep", useBinary ? "Bin" : ""); @@ -5475,7 +5356,7 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv checkProcessColor = ((psProcessBlack & processColors) == 0); } for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); + imgStr.getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); g = colToByte(cmyk.c) + colToByte(cmyk.k); if (checkProcessColor && g > 0) { @@ -5503,6 +5384,9 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } } } else { + // allocate a line buffer + unsigned char *lineBuf = (unsigned char *)gmallocn(width, 4); + for (y = 0; y < height; ++y) { // read the line @@ -5511,7 +5395,7 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } if (checkProcessColor) { for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); + imgStr.getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); lineBuf[4 * x + 0] = colToByte(cmyk.c); lineBuf[4 * x + 1] = colToByte(cmyk.m); @@ -5521,7 +5405,7 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } } else { for (x = 0; x < width; ++x) { - imgStr->getPixel(pixBuf); + imgStr.getPixel(pixBuf); colorMap->getCMYK(pixBuf, &cmyk); lineBuf[4 * x + 0] = colToByte(cmyk.c); lineBuf[4 * x + 1] = colToByte(cmyk.m); @@ -5557,6 +5441,7 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } } } + gfree(lineBuf); } if (i != 0) { @@ -5567,8 +5452,6 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv } str->close(); - delete imgStr; - gfree(lineBuf); if (maskStr && !(maskColors && colorMap)) { writePS("pdfImClipEnd\n"); @@ -5577,15 +5460,16 @@ void PSOutputDev::doImageL1Sep(Object *ref, GfxImageColorMap *colorMap, bool inv void PSOutputDev::maskToClippingPath(Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert) { - ImageStream *imgStr; unsigned char *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; bool emitRect, addRect, extendRect; int i, x0, x1, y, maskXor; - imgStr = new ImageStream(maskStr, maskWidth, 1, 1); - imgStr->reset(); + ImageStream imgStr(maskStr, maskWidth, 1, 1); + if (!imgStr.reset()) { + return; + } rects0Len = rects1Len = rectsOutLen = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); @@ -5593,7 +5477,7 @@ void PSOutputDev::maskToClippingPath(Stream *maskStr, int maskWidth, int maskHei rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); maskXor = maskInvert ? 1 : 0; for (y = 0; y < maskHeight; ++y) { - if (!(line = imgStr->getLine())) { + if (!(line = imgStr.getLine())) { break; } i = 0; @@ -5692,7 +5576,6 @@ void PSOutputDev::maskToClippingPath(Stream *maskStr, int maskWidth, int maskHei gfree(rectsOut); gfree(rects0); gfree(rects1); - delete imgStr; maskStr->close(); } @@ -5700,12 +5583,10 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo bool maskInvert) { Stream *str2; - ImageStream *imgStr; unsigned char *line; PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut; int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize; bool emitRect, addRect, extendRect; - GooString *s; int n, numComps; bool useLZW, useRLE, useASCII, useCompressed; GfxSeparationColorSpace *sepCS; @@ -5722,15 +5603,17 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo // can't read the stream twice for inline images -- but masking // isn't allowed with inline images anyway numComps = colorMap->getNumPixelComps(); - imgStr = new ImageStream(str, width, numComps, colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr(str, width, numComps, colorMap->getBits()); + if (!imgStr.reset()) { + return; + } rects0Len = rects1Len = 0; rectsSize = rectsOutSize = 64; rects0 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rects1 = (PSOutImgClipRect *)gmallocn(rectsSize, sizeof(PSOutImgClipRect)); rectsOut = (PSOutImgClipRect *)gmallocn(rectsOutSize, sizeof(PSOutImgClipRect)); for (y = 0; y < height; ++y) { - if (!(line = imgStr->getLine())) { + if (!(line = imgStr.getLine())) { break; } i = 0; @@ -5857,7 +5740,6 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo gfree(rectsOut); gfree(rects0); gfree(rects1); - delete imgStr; str->close(); // explicit masking @@ -5888,7 +5770,7 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo } else { str2 = new ASCII85Encoder(str2); } - str2->reset(); + (void)str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { @@ -5993,9 +5875,9 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo writePS(" /DataSource currentfile\n"); } + std::optional s; // filters if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { - s = nullptr; useLZW = useRLE = false; useCompressed = false; useASCII = false; @@ -6028,9 +5910,6 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo if (useCompressed) { writePS(s->c_str()); } - if (s) { - delete s; - } if (mode == psModeForm || inType3Char || preloadImagesForms) { @@ -6082,12 +5961,13 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo } else { // need to read the stream to count characters -- the length // is data-dependent (because of ASCII and LZW/RLE filters) - str->reset(); n = 0; - while ((c = str->getChar()) != EOF) { - ++n; + if (str->reset()) { + while ((c = str->getChar()) != EOF) { + ++n; + } + str->close(); } - str->close(); } // +6/7 for "pdfIm\n" / "pdfImM\n" // +8 for newline + trailer @@ -6105,19 +5985,20 @@ void PSOutputDev::doImageL2(GfxState *state, Object *ref, GfxImageColorMap *colo } // copy the stream data - str->reset(); - i = 0; - while ((c = str->getChar()) != EOF) { - dataBuf[i++] = c; - if (i >= (int)sizeof(dataBuf)) { + if (str->reset()) { + i = 0; + while ((c = str->getChar()) != EOF) { + dataBuf[i++] = c; + if (i >= (int)sizeof(dataBuf)) { + writePSBuf(dataBuf, i); + i = 0; + } + } + if (i > 0) { writePSBuf(dataBuf, i); - i = 0; } + str->close(); } - if (i > 0) { - writePSBuf(dataBuf, i); - } - str->close(); // add newline and trailer to the end writePSChar('\n'); @@ -6148,7 +6029,6 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo bool maskInvert) { Stream *str2; - GooString *s; int n, numComps; bool useFlate, useLZW, useRLE, useASCII, useCompressed; bool maskUseFlate, maskUseLZW, maskUseRLE, maskUseASCII, maskUseCompressed; @@ -6166,9 +6046,10 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo // explicit masking if (maskStr) { + std::optional s; // mask data source if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { - s = nullptr; + s = {}; } else { s = maskStr->getPSFilter(3, " "); if (!s) { @@ -6196,11 +6077,8 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } else if (maskUseRLE) { maskFilters->append(" /RunLengthDecode filter\n"); } - if (maskUseCompressed) { - maskFilters->append(s); - } - if (s) { - delete s; + if (maskUseCompressed && s.has_value()) { + maskFilters->append(s.value()); } if (mode == psModeForm || inType3Char || preloadImagesForms) { writePSFmt("MaskData_{0:d}_{1:d} pdfMaskInit\n", ref->getRefNum(), ref->getRefGen()); @@ -6213,12 +6091,9 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo if (maskUseCompressed) { maskStr = maskStr->getUndecodedStream(); } -#ifdef ENABLE_ZLIB if (maskUseFlate) { maskStr = new FlateEncoder(maskStr); - } else -#endif - if (maskUseLZW) { + } else if (maskUseLZW) { maskStr = new LZWEncoder(maskStr); } else if (maskUseRLE) { maskStr = new RunLengthEncoder(maskStr); @@ -6232,7 +6107,7 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } // copy the stream data - maskStr->reset(); + (void)maskStr->reset(); while ((c = maskStr->getChar()) != EOF) { writePSChar(c); } @@ -6260,12 +6135,9 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo if (inlineImg) { // create an array str2 = new FixedLengthEncoder(str, len); -#ifdef ENABLE_ZLIB if (getEnableFlate()) { str2 = new FlateEncoder(str2); - } else -#endif - if (getEnableLZW()) { + } else if (getEnableLZW()) { str2 = new LZWEncoder(str2); } else { str2 = new RunLengthEncoder(str2); @@ -6275,7 +6147,7 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } else { str2 = new ASCII85Encoder(str2); } - str2->reset(); + (void)str2->reset(); col = 0; writePS((char *)(useASCIIHex ? "[<" : "[<~")); do { @@ -6395,8 +6267,9 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo useCompressed = false; useASCII = false; + std::optional s; if ((mode == psModeForm || inType3Char || preloadImagesForms) && uncompressPreloadedImages) { - s = nullptr; + s = {}; } else { s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3, " "); if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) || inlineImg || !s) { @@ -6423,12 +6296,9 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } else if (useRLE) { writePS(" /RunLengthDecode filter\n"); } - if (useCompressed) { + if (useCompressed && s.has_value()) { writePS(s->c_str()); } - if (s) { - delete s; - } // end of image (data) dictionary writePS(">>\n"); @@ -6492,12 +6362,9 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } // add FlateEncode/LZWEncode/RunLengthEncode and ASCIIHex/85 encode filters -#ifdef ENABLE_ZLIB if (useFlate) { str = new FlateEncoder(str); - } else -#endif - if (useLZW) { + } else if (useLZW) { str = new LZWEncoder(str); } else if (useRLE) { str = new RunLengthEncoder(str); @@ -6511,11 +6378,12 @@ void PSOutputDev::doImageL3(GfxState *state, Object *ref, GfxImageColorMap *colo } // copy the stream data - str->reset(); - while ((c = str->getChar()) != EOF) { - writePSChar(c); + if (str->reset()) { + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); } - str->close(); // add newline and trailer to the end writePSChar('\n'); @@ -6656,25 +6524,25 @@ void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, b Ref ref = iccBasedCS->getRef(); const bool validref = ref != Ref::INVALID(); int intent = state->getCmsRenderingIntent(); - std::unique_ptr name; + std::string name; if (validref) { name = GooString::format("ICCBased-{0:d}-{1:d}-{2:d}", ref.num, ref.gen, intent); } else { const unsigned long long hash = std::hash {}(iccBasedCS->getProfile()); name = GooString::format("ICCBased-hashed-{0:ullX}-{1:d}", hash, intent); } - const auto &it = iccEmitted.find(name->toStr()); + const auto &it = iccEmitted.find(name); if (it != iccEmitted.end()) { - writePSFmt("{0:t}", name.get()); + writePSFmt("{0:s}", name.c_str()); if (genXform) { writePS(" {}"); } } else { char *csa = iccBasedCS->getPostScriptCSA(); if (csa) { - writePSFmt("userdict /{0:t} {1:s} put\n", name.get(), csa); - iccEmitted.emplace(name->toStr()); - writePSFmt("{0:t}", name.get()); + writePSFmt("userdict /{0:s} {1:s} put\n", name.c_str(), csa); + iccEmitted.emplace(name); + writePSFmt("{0:s}", name.c_str()); if (genXform) { writePS(" {}"); } @@ -6805,6 +6673,7 @@ void PSOutputDev::dumpColorSpaceL2(GfxState *state, GfxColorSpace *colorSpace, b break; case csPattern: + case csDeviceRGBA: //~ unimplemented break; } @@ -7167,11 +7036,12 @@ void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) } else { str = psStream; } - str->reset(); - while ((c = str->getChar()) != EOF) { - writePSChar(c); + if (str->reset()) { + while ((c = str->getChar()) != EOF) { + writePSChar(c); + } + str->close(); } - str->close(); } //~ can nextFunc be reset to 0 -- maybe at the start of each page? @@ -7186,11 +7056,11 @@ void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) switch (func->getType()) { - case -1: // identity + case Function::Type::Identity: writePS("{}\n"); break; - case 0: // sampled + case Function::Type::Sampled: func0 = (const SampledFunction *)func; thisFunc = nextFunc++; m = func0->getInputSize(); @@ -7264,7 +7134,7 @@ void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) writePS("}\n"); break; - case 2: // exponential + case Function::Type::Exponential: func2 = (const ExponentialFunction *)func; n = func2->getOutputSize(); writePSFmt("{{ dup {0:.6g} lt {{ pop {1:.6g} }} {{ dup {2:.6g} gt {{ pop {3:.6g} }} if }} ifelse\n", func2->getDomainMin(0), func2->getDomainMin(0), func2->getDomainMax(0), func2->getDomainMax(0)); @@ -7287,7 +7157,7 @@ void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) writePS("}\n"); break; - case 3: // stitching + case Function::Type::Stitching: func3 = (const StitchingFunction *)func; thisFunc = nextFunc++; for (i = 0; i < func3->getNumFuncs(); ++i) { @@ -7312,7 +7182,7 @@ void PSOutputDev::cvtFunction(const Function *func, bool invertPSFunction) writePS("}\n"); break; - case 4: // PostScript + case Function::Type::PostScript: func4 = (const PostScriptFunction *)func; if (invertPSFunction) { GooString *codeString = new GooString(func4->getCodeString()); @@ -7376,8 +7246,8 @@ void PSOutputDev::writePSFmt(const char *fmt, ...) if (t3String) { t3String->appendfv((char *)fmt, args); } else { - const std::unique_ptr buf = GooString::formatv((char *)fmt, args); - (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); + const std::string buf = GooString::formatv((char *)fmt, args); + (*outputFunc)(outputStream, buf.c_str(), buf.size()); } va_end(args); } @@ -7427,27 +7297,25 @@ void PSOutputDev::writePSName(const char *s) } } -GooString *PSOutputDev::filterPSName(const std::string &name) +std::string PSOutputDev::filterPSName(const std::string &name) { - GooString *name2; - char buf[8]; - - name2 = new GooString(); + std::string name2; // ghostscript chokes on names that begin with out-of-limits // numbers, e.g., 1e4foo is handled correctly (as a name), but // 1e999foo generates a limitcheck error const char c0 = name[0]; if (c0 >= '0' && c0 <= '9') { - name2->append('f'); + name2 += 'f'; } for (const char c : name) { if (c <= (char)0x20 || c >= (char)0x7f || c == '(' || c == ')' || c == '<' || c == '>' || c == '[' || c == ']' || c == '{' || c == '}' || c == '/' || c == '%') { + char buf[8]; sprintf(buf, "#%02x", c & 0xff); - name2->append(buf); + name2.append(buf); } else { - name2->append(c); + name2 += c; } } return name2; @@ -7504,8 +7372,8 @@ GooString *PSOutputDev::filterPSLabel(GooString *label, bool *needParens) } else if (c == '(') { label2->append("\\("); } else if (c < 0x20 || c > 0x7e) { - std::unique_ptr aux = GooString::format("\\{0:03o}", c); - label2->append(aux.get()); + std::string aux = GooString::format("\\{0:03o}", c); + label2->append(aux); j += 4; } else { label2->append(c); @@ -7519,9 +7387,10 @@ GooString *PSOutputDev::filterPSLabel(GooString *label, bool *needParens) } // Write a DSC-compliant . -void PSOutputDev::writePSTextLine(const GooString *s) +void PSOutputDev::writePSTextLine(const std::string &s) { - int i, j, step; + std::size_t i; + int j, step; int c; // - DSC comments must be printable ASCII; control chars and @@ -7531,15 +7400,15 @@ void PSOutputDev::writePSTextLine(const GooString *s) // for the keyword, which was emitted by the caller) // - lines that start with a left paren are treated as // instead of , so we escape a leading paren - if (s->getLength() >= 2 && (s->getChar(0) & 0xff) == 0xfe && (s->getChar(1) & 0xff) == 0xff) { + if (s.starts_with(unicodeByteOrderMark)) { i = 3; step = 2; } else { i = 0; step = 1; } - for (j = 0; i < s->getLength() && j < 200; i += step) { - c = s->getChar(i) & 0xff; + for (j = 0; i < s.size() && j < 200; i += step) { + c = s[i] & 0xff; if (c == '\\') { writePS("\\\\"); j += 2; diff --git a/poppler/PSOutputDev.h b/poppler/PSOutputDev.h index dfb4ad8..4ad0c0f 100644 --- a/poppler/PSOutputDev.h +++ b/poppler/PSOutputDev.h @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Martin Kretzschmar // Copyright (C) 2005 Kristian Høgsberg -// Copyright (C) 2006-2008, 2012, 2013, 2015, 2017-2022 Albert Astals Cid +// Copyright (C) 2006-2008, 2012, 2013, 2015, 2017-2024 Albert Astals Cid // Copyright (C) 2007 Brad Hards // Copyright (C) 2009-2013 Thomas Freitag // Copyright (C) 2009 Till Kamppeter @@ -27,9 +27,10 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2018, 2020 Philipp Knechtges -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2023, 2024 Oliver Sander // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Christian Persch +// Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -286,10 +287,7 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev xScale0 = x; yScale0 = y; } - void setRotate(int rotateA) - { - rotate0 = rotateA; - } + void setRotate(int rotateA) { rotate0 = rotateA; } void setClip(double llx, double lly, double urx, double ury) { clipLLX0 = llx; @@ -307,144 +305,45 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev overlayCbk = cbk; overlayCbkData = data; } - void setDisplayText(bool display) - { - displayText = display; - } + void setDisplayText(bool display) { displayText = display; } - void setPSCenter(bool center) - { - psCenter = center; - } - void setPSExpandSmaller(bool expand) - { - psExpandSmaller = expand; - } - void setPSShrinkLarger(bool shrink) - { - psShrinkLarger = shrink; - } - void setOverprintPreview(bool overprintPreviewA) - { - overprintPreview = overprintPreviewA; - } - void setRasterAntialias(bool a) - { - rasterAntialias = a; - } - void setForceRasterize(PSForceRasterize f) - { - forceRasterize = f; - } - void setRasterResolution(double r) - { - rasterResolution = r; - } + void setPSCenter(bool center) { psCenter = center; } + void setPSExpandSmaller(bool expand) { psExpandSmaller = expand; } + void setPSShrinkLarger(bool shrink) { psShrinkLarger = shrink; } + void setOverprintPreview(bool overprintPreviewA) { overprintPreview = overprintPreviewA; } + void setRasterAntialias(bool a) { rasterAntialias = a; } + void setForceRasterize(PSForceRasterize f) { forceRasterize = f; } + void setRasterResolution(double r) { rasterResolution = r; } void setRasterMono(bool b) { processColorFormat = splashModeMono8; processColorFormatSpecified = true; } - void setUncompressPreloadedImages(bool b) - { - uncompressPreloadedImages = b; - } - - bool getEmbedType1() const - { - return embedType1; - } - bool getEmbedTrueType() const - { - return embedTrueType; - } - bool getEmbedCIDPostScript() const - { - return embedCIDPostScript; - } - bool getEmbedCIDTrueType() const - { - return embedCIDTrueType; - } - bool getFontPassthrough() const - { - return fontPassthrough; - } - bool getOptimizeColorSpace() const - { - return optimizeColorSpace; - } - bool getPassLevel1CustomColor() const - { - return passLevel1CustomColor; - } - bool getEnableLZW() const - { - return enableLZW; - }; - bool getEnableFlate() const -#ifdef ENABLE_ZLIB - { - return enableFlate; - } -#else - { - return false; - } -#endif - void setEmbedType1(bool b) - { - embedType1 = b; - } - void setEmbedTrueType(bool b) - { - embedTrueType = b; - } - void setEmbedCIDPostScript(bool b) - { - embedCIDPostScript = b; - } - void setEmbedCIDTrueType(bool b) - { - embedCIDTrueType = b; - } - void setFontPassthrough(bool b) - { - fontPassthrough = b; - } - void setOptimizeColorSpace(bool b) - { - optimizeColorSpace = b; - } - void setPassLevel1CustomColor(bool b) - { - passLevel1CustomColor = b; - } - void setPreloadImagesForms(bool b) - { - preloadImagesForms = b; - } - void setGenerateOPI(bool b) - { - generateOPI = b; - } - void setUseASCIIHex(bool b) - { - useASCIIHex = b; - } - void setUseBinary(bool b) - { - useBinary = b; - } - void setEnableLZW(bool b) - { - enableLZW = b; - } - void setEnableFlate(bool b) - { - enableFlate = b; - } + void setUncompressPreloadedImages(bool b) { uncompressPreloadedImages = b; } + + bool getEmbedType1() const { return embedType1; } + bool getEmbedTrueType() const { return embedTrueType; } + bool getEmbedCIDPostScript() const { return embedCIDPostScript; } + bool getEmbedCIDTrueType() const { return embedCIDTrueType; } + bool getFontPassthrough() const { return fontPassthrough; } + bool getOptimizeColorSpace() const { return optimizeColorSpace; } + bool getPassLevel1CustomColor() const { return passLevel1CustomColor; } + bool getEnableLZW() const { return enableLZW; }; + bool getEnableFlate() const { return enableFlate; } + void setEmbedType1(bool b) { embedType1 = b; } + void setEmbedTrueType(bool b) { embedTrueType = b; } + void setEmbedCIDPostScript(bool b) { embedCIDPostScript = b; } + void setEmbedCIDTrueType(bool b) { embedCIDTrueType = b; } + void setFontPassthrough(bool b) { fontPassthrough = b; } + void setOptimizeColorSpace(bool b) { optimizeColorSpace = b; } + void setPassLevel1CustomColor(bool b) { passLevel1CustomColor = b; } + void setPreloadImagesForms(bool b) { preloadImagesForms = b; } + void setGenerateOPI(bool b) { generateOPI = b; } + void setUseASCIIHex(bool b) { useASCIIHex = b; } + void setUseBinary(bool b) { useBinary = b; } + void setEnableLZW(bool b) { enableLZW = b; } + void setEnableFlate(bool b) { enableFlate = b; } void setProcessColorFormat(SplashColorMode format) { @@ -453,6 +352,16 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev } private: + struct PSOutPaperSize + { + PSOutPaperSize() = default; + PSOutPaperSize(std::string &&nameA, int wA, int hA) : name(nameA), w(wA), h(hA) { } + ~PSOutPaperSize() = default; + PSOutPaperSize &operator=(const PSOutPaperSize &) = delete; + std::string name; + int w, h; + }; + void init(FoFiOutputFunc outputFuncA, void *outputStreamA, PSFileType fileTypeA, char *psTitleA, PDFDoc *doc, const std::vector &pages, PSOutMode modeA, int imgLLXA, int imgLLYA, int imgURXA, int imgURYA, bool manualCtrlA, int paperWidthA, int paperHeightA, bool noCropA, bool duplexA, PSLevel levelA); void postInit(); @@ -461,17 +370,17 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev void setupFont(GfxFont *font, Dict *parentResDict); void setupEmbeddedType1Font(Ref *id, GooString *psName); void updateFontMaxValidGlyph(GfxFont *font, int maxValidGlyph); - void setupExternalType1Font(const GooString *fileName, GooString *psName); + void setupExternalType1Font(const std::string &fileName, GooString *psName); void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GooString *psName); - void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName); - void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName); - void setupExternalTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName); + void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex); + void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex); + void setupExternalTrueTypeFont(GfxFont *font, const std::string &fileName, GooString *psName, int faceIndex); void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GooString *psName); - void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics); - void setupExternalCIDTrueTypeFont(GfxFont *font, const GooString *fileName, GooString *psName, bool needVerticalMetrics); - void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName); + void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GooString *psName, bool needVerticalMetrics, int faceIndex); + void setupExternalCIDTrueTypeFont(GfxFont *font, const std::string &fileName, GooString *psName, bool needVerticalMetrics, int faceIndex); + void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GooString *psName, int faceIndex); void setupType3Font(GfxFont *font, GooString *psName, Dict *parentResDict); - GooString *makePSFontName(GfxFont *font, const Ref *id); + std::unique_ptr makePSFontName(GfxFont *font, const Ref *id); void setupImages(Dict *resDict); void setupImage(Ref id, Stream *str, bool mask); void setupForms(Dict *resDict); @@ -494,7 +403,7 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev void opiTransform(GfxState *state, double x0, double y0, double *x1, double *y1); #endif void cvtFunction(const Function *func, bool invertPSFunction = false); - GooString *filterPSName(const std::string &name); + static std::string filterPSName(const std::string &name); // Write the document-level setup. void writeDocSetup(Catalog *catalog, const std::vector &pageList, bool duplexA); @@ -502,11 +411,11 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev void writePSChar(char c); void writePS(const char *s); void writePSBuf(const char *s, int len); - void writePSFmt(const char *fmt, ...); + void writePSFmt(const char *fmt, ...) GOOSTRING_FORMAT; void writePSString(const std::string &s); void writePSName(const char *s); GooString *filterPSLabel(GooString *label, bool *needParens = nullptr); - void writePSTextLine(const GooString *s); + void writePSTextLine(const std::string &s); PSLevel level; // PostScript level (1, 2, separation) PSOutMode mode; // PostScript mode (PS, EPS, form) @@ -540,18 +449,12 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev PDFDoc *doc; XRef *xref; // the xref table for this PDF file - Ref *fontIDs; // list of object IDs of all used fonts - int fontIDLen; // number of entries in fontIDs array - int fontIDSize; // size of fontIDs array + std::vector fontIDs; // list of object IDs of all used fonts std::set resourceIDs; // list of object IDs of objects containing Resources we've already set up std::unordered_set fontNames; // all used font names std::unordered_map perFontMaxValidGlyph; // max valid glyph of each font - PST1FontName *t1FontNames; // font names for Type 1/1C fonts - int t1FontNameLen; // number of entries in t1FontNames array - int t1FontNameSize; // size of t1FontNames array - PSFont8Info *font8Info; // info for 8-bit fonts - int font8InfoLen; // number of entries in font8Info array - int font8InfoSize; // size of font8Info array + std::vector t1FontNames; // font names for Type 1/1C fonts + std::vector font8Info; // info for 8-bit fonts PSFont16Enc *font16Enc; // encodings for substitute 16-bit fonts int font16EncLen; // number of entries in font16Enc array int font16EncSize; // size of font16Enc array @@ -565,8 +468,8 @@ class POPPLER_PRIVATE_EXPORT PSOutputDev : public OutputDev int numTilingPatterns; // current number of nested tiling patterns int nextFunc; // next unique number to use for a function - std::vector *paperSizes; // list of used paper sizes, if paperMatch - // is true + std::vector paperSizes; // list of used paper sizes, if paperMatch + // is true std::map pagePaperSize; // page num to paperSize entry mapping double tx0, ty0; // global translation double xScale0, yScale0; // global scaling diff --git a/poppler/PSTokenizer.cc b/poppler/PSTokenizer.cc index 7d614bb..827cdfd 100644 --- a/poppler/PSTokenizer.cc +++ b/poppler/PSTokenizer.cc @@ -60,8 +60,6 @@ PSTokenizer::PSTokenizer(int (*getCharFuncA)(void *), void *dataA) charBuf = -1; } -PSTokenizer::~PSTokenizer() { } - bool PSTokenizer::getToken(char *buf, int size, int *length) { bool comment, backslash; diff --git a/poppler/PSTokenizer.h b/poppler/PSTokenizer.h index dab684b..c4488a5 100644 --- a/poppler/PSTokenizer.h +++ b/poppler/PSTokenizer.h @@ -29,7 +29,7 @@ class PSTokenizer { public: PSTokenizer(int (*getCharFuncA)(void *), void *dataA); - ~PSTokenizer(); + ~PSTokenizer() = default; // Get the next PostScript token. Returns false at end-of-stream. bool getToken(char *buf, int size, int *length); diff --git a/poppler/Page.cc b/poppler/Page.cc index c256d39..90681bc 100644 --- a/poppler/Page.cc +++ b/poppler/Page.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Kristian Høgsberg // Copyright (C) 2005 Jeff Muizelaar -// Copyright (C) 2005-2013, 2016-2022 Albert Astals Cid +// Copyright (C) 2005-2013, 2016-2025 Albert Astals Cid // Copyright (C) 2006-2008 Pino Toscano // Copyright (C) 2006 Nickolay V. Shmyrev // Copyright (C) 2006 Scott Turner @@ -27,12 +27,14 @@ // Copyright (C) 2012, 2013 Fabio D'Urso // Copyright (C) 2013, 2014 Thomas Freitag // Copyright (C) 2013 Jason Crain -// Copyright (C) 2013, 2017 Adrian Johnson +// Copyright (C) 2013, 2017, 2023 Adrian Johnson // Copyright (C) 2015 Philipp Reinkemeier // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2020 Oliver Sander // Copyright (C) 2020, 2021 Nelson Benítez León // Copyright (C) 2020 Philipp Knechtges +// Copyright (C) 2024 Pablo Correa Gómez +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -59,7 +61,6 @@ #include "Error.h" #include "Page.h" #include "Catalog.h" -#include "Form.h" //------------------------------------------------------------------------ // PDFRectangle @@ -93,7 +94,7 @@ void PDFRectangle::clipTo(PDFRectangle *rect) // PageAttrs //------------------------------------------------------------------------ -PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) +PageAttrs::PageAttrs(const PageAttrs *attrs, Dict *dict) { Object obj1; PDFRectangle mBox; @@ -179,7 +180,7 @@ PageAttrs::PageAttrs(PageAttrs *attrs, Dict *dict) } } -PageAttrs::~PageAttrs() { } +PageAttrs::~PageAttrs() = default; void PageAttrs::clipBoxes() { @@ -251,7 +252,7 @@ bool PageAttrs::readBox(Dict *dict, const char *key, PDFRectangle *box) #define pageLocker() const std::scoped_lock locker(mutex) -Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form) +Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, std::unique_ptr attrsA, Form *form) : pageRef(pageRefA), attrs(std::move(attrsA)) { ok = true; doc = docA; @@ -259,12 +260,11 @@ Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *a num = numA; duration = -1; annots = nullptr; + structParents = -1; pageObj = std::move(pageDict); - pageRef = pageRefA; // get attributes - attrs = attrsA; attrs->clipBoxes(); // transtion @@ -282,6 +282,14 @@ Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *a duration = tmp.getNum(); } + // structParents + const Object &tmp2 = pageObj.dictLookup("StructParents"); + if (!(tmp2.isInt() || tmp2.isNull())) { + error(errSyntaxError, -1, "Page StructParents object (page {0:d}) is wrong type ({1:s})", num, tmp2.getTypeName()); + } else if (tmp2.isInt()) { + structParents = tmp2.getInt(); + } + // annotations annotsObj = pageObj.dictLookupNF("Annots").copy(); if (!(annotsObj.isRef() || annotsObj.isArray() || annotsObj.isNull())) { @@ -321,11 +329,7 @@ Page::Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *a Page::~Page() { - delete attrs; delete annots; - for (auto frmField : standaloneFields) { - delete frmField; - } } Dict *Page::getResourceDict() @@ -381,11 +385,11 @@ void Page::loadStandaloneFields(Annots *annotations, Form *form) } std::set parents; - FormField *field = Form::createFieldFromDict(annot->getAnnotObj().copy(), annot->getDoc(), r, nullptr, &parents); + std::unique_ptr field = Form::createFieldFromDict(annot->getAnnotObj().copy(), annot->getDoc(), r, nullptr, &parents); if (field && field->getNumWidgets() == 1) { - static_cast(annot)->setField(field); + static_cast(annot)->setField(field.get()); field->setStandAlone(true); FormWidget *formWidget = field->getWidget(0); @@ -394,10 +398,7 @@ void Page::loadStandaloneFields(Annots *annotations, Form *form) formWidget->createWidgetAnnotation(); } - standaloneFields.push_back(field); - - } else if (field) { - delete field; + standaloneFields.push_back(std::move(field)); } } } @@ -414,8 +415,14 @@ Annots *Page::getAnnots(XRef *xrefA) return annots; } -void Page::addAnnot(Annot *annot) +bool Page::addAnnot(Annot *annot) { + if (unlikely(xref->getEntry(pageRef.num)->type == xrefEntryFree)) { + // something very wrong happened if we're here + error(errInternal, -1, "Can not addAnnot to page with an invalid ref"); + return false; + } + const Ref annotRef = annot->getRef(); // Make sure we have annots before adding the new one @@ -463,6 +470,8 @@ void Page::addAnnot(Annot *annot) addAnnot(annotPopup); } } + + return true; } void Page::removeAnnot(Annot *annot) @@ -490,7 +499,6 @@ void Page::removeAnnot(Annot *annot) } annots->removeAnnot(annot); // Gracefully fails on popup windows annArray.arrayRemove(idx); - xref->removeIndirectObject(annotRef); if (annotsObj.isRef()) { xref->setModifiedObject(&annArray, annotsObj.getRef()); @@ -499,6 +507,9 @@ void Page::removeAnnot(Annot *annot) } } annot->removeReferencedObjects(); // Note: Might recurse in removeAnnot again + if (annArray.isArray()) { + xref->removeIndirectObject(annotRef); + } annot->setPage(0, false); } @@ -521,11 +532,10 @@ void Page::display(OutputDev *out, double hDPI, double vDPI, int rotate, bool us displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop, -1, -1, -1, -1, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData, copyXRef); } -Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, XRef *xrefA) +std::unique_ptr Page::createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, XRef *xrefA) { const PDFRectangle *mediaBox, *cropBox; PDFRectangle box; - Gfx *gfx; rotate += getRotate(); if (rotate >= 360) { @@ -547,15 +557,12 @@ Gfx *Page::createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool if (!crop) { crop = (box == *cropBox) && out->needClipToCropBox(); } - gfx = new Gfx(doc, out, num, attrs->getResourceDict(), hDPI, vDPI, &box, crop ? cropBox : nullptr, rotate, abortCheckCbk, abortCheckCbkData, xrefA); - - return gfx; + return std::make_unique(doc, out, num, attrs->getResourceDict(), hDPI, vDPI, &box, crop ? cropBox : nullptr, rotate, abortCheckCbk, abortCheckCbkData, xrefA); } void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, bool (*annotDisplayDecideCbk)(Annot *annot, void *user_data), void *annotDisplayDecideCbkData, bool copyXRef) { - Gfx *gfx; Annots *annotList; if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, annotDisplayDecideCbk, annotDisplayDecideCbkData)) { @@ -567,7 +574,7 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, bo replaceXRef(localXRef); } - gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, printing, abortCheckCbk, abortCheckCbkData, localXRef); + std::unique_ptr gfx = createGfx(out, hDPI, vDPI, rotate, useMediaBox, crop, sliceX, sliceY, sliceW, sliceH, abortCheckCbk, abortCheckCbkData, localXRef); Object obj = contents.fetch(localXRef); if (!obj.isNull()) { @@ -589,13 +596,12 @@ void Page::displaySlice(OutputDev *out, double hDPI, double vDPI, int rotate, bo } for (Annot *annot : annots->getAnnots()) { if ((annotDisplayDecideCbk && (*annotDisplayDecideCbk)(annot, annotDisplayDecideCbkData)) || !annotDisplayDecideCbk) { - annot->draw(gfx, printing); + annot->draw(gfx.get(), printing); } } out->dump(); } - delete gfx; if (copyXRef) { replaceXRef(doc->getXRef()); delete localXRef; @@ -618,9 +624,7 @@ bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, int width, height, bits; Object obj1; Dict *dict; - GfxColorSpace *colorSpace; Stream *str; - GfxImageColorMap *colorMap; /* Get stream dict */ pageLocker(); @@ -660,7 +664,7 @@ bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, // This will set a sRGB profile for ICC-based colorspaces. auto pdfrectangle = std::make_shared(); auto state = std::make_shared(72.0, 72.0, pdfrectangle.get(), 0, false); - colorSpace = GfxColorSpace::parse(nullptr, &obj1, nullptr, state.get()); + std::unique_ptr colorSpace = GfxColorSpace::parse(nullptr, &obj1, nullptr, state.get()); if (!colorSpace) { fprintf(stderr, "Error: Cannot parse color space\n"); return false; @@ -670,25 +674,26 @@ bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, if (obj1.isNull()) { obj1 = dict->lookup("D"); } - colorMap = new GfxImageColorMap(bits, &obj1, colorSpace); - if (!colorMap->isOk()) { + GfxImageColorMap colorMap(bits, &obj1, std::move(colorSpace)); + if (!colorMap.isOk()) { fprintf(stderr, "Error: invalid colormap\n"); - delete colorMap; return false; } if (data_out) { + ImageStream imgstr { str, width, colorMap.getNumPixelComps(), colorMap.getBits() }; + if (!imgstr.reset()) { + return false; + } unsigned char *pixbufdata = (unsigned char *)gmalloc(pixbufdatasize); unsigned char *p = pixbufdata; - ImageStream *imgstr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgstr->reset(); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { unsigned char pix[gfxColorMaxComps]; GfxRGB rgb; - imgstr->getPixel(pix); - colorMap->getRGB(pix, &rgb); + imgstr.getPixel(pix); + colorMap.getRGB(pix, &rgb); *p++ = colToByte(rgb.r); *p++ = colToByte(rgb.g); @@ -696,8 +701,7 @@ bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, } } *data_out = pixbufdata; - imgstr->close(); - delete imgstr; + imgstr.close(); } if (width_out) { @@ -709,9 +713,6 @@ bool Page::loadThumb(unsigned char **data_out, int *width_out, int *height_out, if (rowstride_out) { *rowstride_out = width * 3; } - - delete colorMap; - return true; } diff --git a/poppler/Page.h b/poppler/Page.h index fbd2c16..2f141a3 100644 --- a/poppler/Page.h +++ b/poppler/Page.h @@ -20,12 +20,15 @@ // Copyright (C) 2007 Julien Rebetez // Copyright (C) 2008 Iñigo Martínez // Copyright (C) 2012 Fabio D'Urso -// Copyright (C) 2012, 2017, 2018, 2020, 2021 Albert Astals Cid +// Copyright (C) 2012, 2017, 2018, 2020, 2021, 2023 Albert Astals Cid // Copyright (C) 2013 Thomas Freitag -// Copyright (C) 2013, 2017 Adrian Johnson +// Copyright (C) 2013, 2017, 2023 Adrian Johnson // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Oliver Sander // Copyright (C) 2020, 2021 Nelson Benítez León +// Copyright (C) 2024 Pablo Correa Gómez +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2025 Even Rouault // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -87,7 +90,7 @@ class PageAttrs // Construct a new PageAttrs object by merging a dictionary // (of type Pages or Page) into another PageAttrs object. If // is nullptr, uses defaults. - PageAttrs(PageAttrs *attrs, Dict *dict); + PageAttrs(const PageAttrs *attrs, Dict *dict); // Destructor. ~PageAttrs(); @@ -140,7 +143,7 @@ class POPPLER_PRIVATE_EXPORT Page { public: // Constructor. - Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, PageAttrs *attrsA, Form *form); + Page(PDFDoc *docA, int numA, Object &&pageDict, Ref pageRefA, std::unique_ptr attrsA, Form *form); // Destructor. ~Page(); @@ -173,6 +176,9 @@ class POPPLER_PRIVATE_EXPORT Page PDFDoc *getDoc() { return doc; } Ref getRef() { return pageRef; } + // Keep in API. This is used by GDAL + const Object &getPageObj() const { return pageObj; } + // Get resource dictionary. Dict *getResourceDict(); Object *getResourceDictObject(); @@ -181,7 +187,7 @@ class POPPLER_PRIVATE_EXPORT Page // Get annotations array. Object getAnnotsObject(XRef *xrefA = nullptr) { return annotsObj.fetch(xrefA ? xrefA : xref); } // Add a new annotation to the page - void addAnnot(Annot *annot); + bool addAnnot(Annot *annot); // Remove an existing annotation from the page void removeAnnot(Annot *annot); @@ -220,8 +226,8 @@ class POPPLER_PRIVATE_EXPORT Page std::unique_ptr getAdditionalAction(PageAdditionalActionsType type); - Gfx *createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool printing, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, - XRef *xrefA = nullptr); + std::unique_ptr createGfx(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, int sliceX, int sliceY, int sliceW, int sliceH, bool (*abortCheckCbk)(void *data), void *abortCheckCbkData, + XRef *xrefA = nullptr); // Display a page. void display(OutputDev *out, double hDPI, double vDPI, int rotate, bool useMediaBox, bool crop, bool printing, bool (*abortCheckCbk)(void *data) = nullptr, void *abortCheckCbkData = nullptr, @@ -242,6 +248,10 @@ class POPPLER_PRIVATE_EXPORT Page bool hasStandaloneFields() const { return !standaloneFields.empty(); } + // Get the integer key of the page's entry in the structural parent tree. + // Returns -1 if the page dict does not contain a StructParents key. + int getStructParents() const { return structParents; } + private: // replace xref void replaceXRef(XRef *xrefA); @@ -249,9 +259,9 @@ class POPPLER_PRIVATE_EXPORT Page PDFDoc *doc; XRef *xref; // the xref table for this PDF file Object pageObj; // page dictionary - Ref pageRef; // page reference + const Ref pageRef; // page reference int num; // page number - PageAttrs *attrs; // page attributes + std::unique_ptr attrs; // page attributes Annots *annots; // annotations Object annotsObj; // annotations array Object contents; // page contents @@ -259,6 +269,7 @@ class POPPLER_PRIVATE_EXPORT Page Object trans; // page transition Object actions; // page additional actions double duration; // page duration + int structParents; // integer key of page in structure parent tree bool ok; // true if page is valid mutable std::recursive_mutex mutex; // standalone widgets are special FormWidget's inside a Page that *are not* @@ -266,7 +277,7 @@ class POPPLER_PRIVATE_EXPORT Page // i.e. the PDF document does not have a FormField associated with them. We // create standalone FormFields to contain those special FormWidgets, as // they are 'de facto' being used to implement tooltips. See #34 - std::vector standaloneFields; + std::vector> standaloneFields; void loadStandaloneFields(Annots *annotations, Form *form); }; diff --git a/poppler/PageLabelInfo.cc b/poppler/PageLabelInfo.cc index 40a6e7f..646c37e 100644 --- a/poppler/PageLabelInfo.cc +++ b/poppler/PageLabelInfo.cc @@ -3,10 +3,11 @@ // This file is under the GPLv2 or later license // // Copyright (C) 2005-2006 Kristian Høgsberg -// Copyright (C) 2005, 2009, 2013, 2017, 2018, 2020, 2021 Albert Astals Cid +// Copyright (C) 2005, 2009, 2013, 2017, 2018, 2020, 2021, 2023 Albert Astals Cid // Copyright (C) 2011 Simon Kellner // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2018 Adam Reichold +// Copyright (C) 2024 Oliver Sander // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -60,7 +61,7 @@ PageLabelInfo::Interval::Interval(Object *dict, int baseA) PageLabelInfo::PageLabelInfo(Object *tree, int numPages) { - std::set alreadyParsedRefs; + RefRecursionChecker alreadyParsedRefs; parse(tree, alreadyParsedRefs); if (intervals.empty()) { @@ -74,7 +75,7 @@ PageLabelInfo::PageLabelInfo(Object *tree, int numPages) curr->length = std::max(0, numPages - curr->base); } -void PageLabelInfo::parse(const Object *tree, std::set &alreadyParsedRefs) +void PageLabelInfo::parse(const Object *tree, RefRecursionChecker &alreadyParsedRefs) { // leaf node Object nums = tree->dictLookup("Nums"); @@ -103,13 +104,9 @@ void PageLabelInfo::parse(const Object *tree, std::set &alreadyParsedRefs) for (int i = 0; i < kidsArray->getLength(); ++i) { Ref ref; const Object kid = kidsArray->get(i, &ref); - if (ref != Ref::INVALID()) { - const int numObj = ref.num; - if (alreadyParsedRefs.find(numObj) != alreadyParsedRefs.end()) { - error(errSyntaxError, -1, "loop in PageLabelInfo (numObj: {0:d})", numObj); - continue; - } - alreadyParsedRefs.insert(numObj); + if (!alreadyParsedRefs.insert(ref)) { + error(errSyntaxError, -1, "loop in PageLabelInfo (ref.num: {0:d})", ref.num); + continue; } if (kid.isDict()) { parse(&kid, alreadyParsedRefs); @@ -122,7 +119,7 @@ bool PageLabelInfo::labelToIndex(GooString *label, int *index) const { const char *const str = label->c_str(); const std::size_t strLen = label->getLength(); - const bool strUnicode = label->hasUnicodeMarker(); + const bool strUnicode = hasUnicodeByteOrderMark(label->toStr()); int number; bool ok; @@ -215,7 +212,7 @@ bool PageLabelInfo::indexToLabel(int index, GooString *label) const label->clear(); label->append(matching_interval->prefix.c_str(), matching_interval->prefix.size()); - if (label->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(label->toStr())) { int i, len; char ucs2_char[2]; diff --git a/poppler/PageLabelInfo.h b/poppler/PageLabelInfo.h index bec70d3..f06fcdc 100644 --- a/poppler/PageLabelInfo.h +++ b/poppler/PageLabelInfo.h @@ -3,7 +3,7 @@ // This file is under the GPLv2 or later license // // Copyright (C) 2005-2006 Kristian Høgsberg -// Copyright (C) 2005, 2018-2020 Albert Astals Cid +// Copyright (C) 2005, 2018-2020, 2023 Albert Astals Cid // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Oliver Sander // @@ -37,9 +37,8 @@ class PageLabelInfo bool indexToLabel(int index, GooString *label) const; private: - void parse(const Object *tree, std::set &parsedRefs); + void parse(const Object *tree, RefRecursionChecker &parsedRefs); -private: struct Interval { Interval(Object *dict, int baseA); diff --git a/poppler/PageLabelInfo_p.h b/poppler/PageLabelInfo_p.h index 5fa1a5e..9d43ad2 100644 --- a/poppler/PageLabelInfo_p.h +++ b/poppler/PageLabelInfo_p.h @@ -7,7 +7,7 @@ // Copyright (C) 2011 Simon Kellner // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2024 Oliver Sander // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -23,11 +23,12 @@ #include "goo/GooString.h" #include "Error.h" +#include "UTF.h" static std::pair fromDecimal(const std::string &str, const bool unicode) { if (unicode && (str.size() % 2 == 0)) { - if (GooString::hasUnicodeMarker(str)) { + if (hasUnicodeByteOrderMark(str)) { // strip the marker if it is there return fromDecimal(str.substr(2), true /*unicode*/); } diff --git a/poppler/PageTransition.cc b/poppler/PageTransition.cc index 0ac7ae6..e1d686e 100644 --- a/poppler/PageTransition.cc +++ b/poppler/PageTransition.cc @@ -129,5 +129,3 @@ PageTransition::PageTransition(Object *trans) // get rectangular rectangular = dict->lookup("B").getBoolWithDefaultValue(false); } - -PageTransition::~PageTransition() { } diff --git a/poppler/PageTransition.h b/poppler/PageTransition.h index b8d51a9..9d27b83 100644 --- a/poppler/PageTransition.h +++ b/poppler/PageTransition.h @@ -65,7 +65,7 @@ class POPPLER_PRIVATE_EXPORT PageTransition explicit PageTransition(Object *trans); // Destructor. - ~PageTransition(); + ~PageTransition() = default; // Was the Page Transition created successfully? bool isOk() const { return ok; } diff --git a/poppler/Parser.cc b/poppler/Parser.cc index e0891e2..8dea86e 100644 --- a/poppler/Parser.cc +++ b/poppler/Parser.cc @@ -22,6 +22,8 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018, 2019 Adam Reichold // Copyright (C) 2018 Marek Kasik +// Copyright (C) 2024 Nelson Benítez León +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -70,7 +72,9 @@ Object Parser::getObj(int recursion) static std::unique_ptr decryptedString(const GooString *s, const unsigned char *fileKey, CryptAlgorithm encAlgorithm, int keyLength, int objNum, int objGen) { DecryptStream decrypt(new MemStream(s->c_str(), 0, s->getLength(), Object(objNull)), fileKey, encAlgorithm, keyLength, { objNum, objGen }); - decrypt.reset(); + if (!decrypt.reset()) { + return {}; + } std::unique_ptr res = std::make_unique(); int c; while ((c = decrypt.getChar()) != EOF) { @@ -142,7 +146,7 @@ Object Parser::getObj(bool simpleOnly, const unsigned char *fileKey, CryptAlgori const bool isContents = !hasContentsEntry && key.isName("Contents"); hasContentsEntry = hasContentsEntry || isContents; Object obj2 = getObj(false, fileKey, encAlgorithm, keyLength, objNum, objGen, recursion + 1, /*strict*/ false, /*decryptString*/ !isContents); - if (unlikely(obj2.isError() && recursion + 1 >= recursionLimit)) { + if (unlikely(recursion + 1 >= recursionLimit)) { break; } obj.dictAdd(key.getName(), std::move(obj2)); @@ -161,7 +165,7 @@ Object Parser::getObj(bool simpleOnly, const unsigned char *fileKey, CryptAlgori const Object &contentsObj = dict->lookupNF("Contents"); if (contentsObj.isString()) { std::unique_ptr s = decryptedString(contentsObj.getString(), fileKey, encAlgorithm, keyLength, objNum, objGen); - dict->set("Contents", Object(s.release())); + dict->set("Contents", Object(std::move(s))); } } } @@ -201,7 +205,7 @@ Object Parser::getObj(bool simpleOnly, const unsigned char *fileKey, CryptAlgori // string } else if (decryptString && buf1.isString() && fileKey) { std::unique_ptr s2 = decryptedString(buf1.getString(), fileKey, encAlgorithm, keyLength, objNum, objGen); - obj = Object(s2.release()); + obj = Object(std::move(s2)); shift(); // simple object diff --git a/poppler/PopplerCache.h b/poppler/PopplerCache.h index 68db153..b291309 100644 --- a/poppler/PopplerCache.h +++ b/poppler/PopplerCache.h @@ -5,7 +5,7 @@ // This file is licensed under the GPLv2 or later // // Copyright (C) 2009 Koji Otani -// Copyright (C) 2009, 2010, 2017, 2018, 2021 Albert Astals Cid +// Copyright (C) 2009, 2010, 2017, 2018, 2021, 2024 Albert Astals Cid // Copyright (C) 2010 Carlos Garcia Campos // Copyright (C) 2018 Adam Reichold // @@ -58,6 +58,16 @@ class PopplerCache entries.emplace(entries.begin(), key, std::unique_ptr { item }); } + /* The key and item pointers ownership is taken by the cache */ + void put(const Key &key, std::unique_ptr &&item) + { + if (entries.size() == entries.capacity()) { + entries.pop_back(); + } + + entries.emplace(entries.begin(), key, std::move(item)); + } + private: std::vector>> entries; }; diff --git a/poppler/PreScanOutputDev.cc b/poppler/PreScanOutputDev.cc index c3ddc5c..2c4b206 100644 --- a/poppler/PreScanOutputDev.cc +++ b/poppler/PreScanOutputDev.cc @@ -19,6 +19,7 @@ // Copyright (C) 2011, 2013 Thomas Freitag // Copyright (C) 2011 Adrian Johnson // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -45,7 +46,7 @@ PreScanOutputDev::PreScanOutputDev(PSLevel levelA) : level(levelA) clearStats(); } -PreScanOutputDev::~PreScanOutputDev() { } +PreScanOutputDev::~PreScanOutputDev() = default; void PreScanOutputDev::startPage(int /*pageNum*/, GfxState * /*state*/, XRef * /*xref*/) { } @@ -57,7 +58,7 @@ void PreScanOutputDev::stroke(GfxState *state) check(state->getStrokeColorSpace(), state->getStrokeColor(), state->getStrokeOpacity(), state->getBlendMode()); const std::vector &dash = state->getLineDash(&dashStart); - if (dash.size() != 0) { + if (!dash.empty()) { gdi = false; } } @@ -183,7 +184,9 @@ void PreScanOutputDev::drawImageMask(GfxState *state, Object * /*ref*/, Stream * } if (inlineImg) { - str->reset(); + if (!str->reset()) { + return; + } j = height * ((width + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); @@ -218,7 +221,9 @@ void PreScanOutputDev::drawImage(GfxState *state, Object * /*ref*/, Stream *str, } if (inlineImg) { - str->reset(); + if (!str->reset()) { + return; + } j = height * ((width * colorMap->getNumPixelComps() * colorMap->getBits() + 7) / 8); for (i = 0; i < j; ++i) { str->getChar(); diff --git a/poppler/Rendition.cc b/poppler/Rendition.cc index 7ca82ff..f1214a5 100644 --- a/poppler/Rendition.cc +++ b/poppler/Rendition.cc @@ -7,7 +7,8 @@ // Pino Toscano (c) 2008 // Carlos Garcia Campos (c) 2010 // Tobias Koenig (c) 2012 -// Albert Astals Cid (C) 2017, 2018 +// Albert Astals Cid (C) 2017, 2018, 2024 +// g10 Code GmbH, Author: Sune Stolborg Vuorela (C) 2025 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -42,8 +43,6 @@ MediaWindowParameters::MediaWindowParameters() isResizeable = true; } -MediaWindowParameters::~MediaWindowParameters() { } - void MediaWindowParameters::parseFWParams(Object *obj) { Object tmp = obj->dictLookup("D"); @@ -150,8 +149,6 @@ MediaParameters::MediaParameters() duration = 0; } -MediaParameters::~MediaParameters() { } - void MediaParameters::parseMediaPlayParameters(Object *obj) { Object tmp = obj->dictLookup("V"); @@ -273,19 +270,13 @@ void MediaParameters::parseMediaScreenParameters(Object *obj) } } -MediaRendition::~MediaRendition() -{ - delete fileName; - delete contentType; -} +MediaRendition::~MediaRendition() = default; MediaRendition::MediaRendition(Object *obj) { bool hasClip = false; ok = true; - fileName = nullptr; - contentType = nullptr; isEmbedded = false; // @@ -378,14 +369,10 @@ MediaRendition::MediaRendition(const MediaRendition &other) if (other.contentType) { contentType = other.contentType->copy(); - } else { - contentType = nullptr; } if (other.fileName) { fileName = other.fileName->copy(); - } else { - fileName = nullptr; } } @@ -395,7 +382,9 @@ void MediaRendition::outputToFile(FILE *fp) return; } - embeddedStreamObject.streamReset(); + if (!embeddedStreamObject.streamReset()) { + return; + } while (true) { int c = embeddedStreamObject.streamGetChar(); @@ -407,9 +396,9 @@ void MediaRendition::outputToFile(FILE *fp) } } -MediaRendition *MediaRendition::copy() const +std::unique_ptr MediaRendition::copy() const { - return new MediaRendition(*this); + return std::make_unique(*this); } // TODO: SelectorRendition diff --git a/poppler/Rendition.h b/poppler/Rendition.h index 1a14c39..3d998dc 100644 --- a/poppler/Rendition.h +++ b/poppler/Rendition.h @@ -5,7 +5,7 @@ //--------------------------------------------------------------------------------- // Hugo Mercier (c) 2008 // Carlos Garcia Campos (c) 2010 -// Albert Astals Cid (C) 2017, 2018, 2021 +// Albert Astals Cid (C) 2017, 2018, 2021, 2024 // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ struct MediaWindowParameters { MediaWindowParameters(); - ~MediaWindowParameters(); + ~MediaWindowParameters() = default; // parse from a floating window parameters dictionary void parseFWParams(Object *obj); @@ -72,7 +72,7 @@ struct MediaParameters { MediaParameters(); - ~MediaParameters(); + ~MediaParameters() = default; // parse from a "Media Play Parameters" dictionary void parseMediaPlayParameters(Object *playObj); @@ -131,8 +131,8 @@ class POPPLER_PRIVATE_EXPORT MediaRendition const MediaParameters *getMHParameters() const { return &MH; } const MediaParameters *getBEParameters() const { return &BE; } - const GooString *getContentType() const { return contentType; } - const GooString *getFileName() const { return fileName; } + const GooString *getContentType() const { return contentType.get(); } + const GooString *getFileName() const { return fileName.get(); } bool getIsEmbedded() const { return isEmbedded; } Stream *getEmbbededStream() const { return isEmbedded ? embeddedStreamObject.getStream() : nullptr; } @@ -140,7 +140,7 @@ class POPPLER_PRIVATE_EXPORT MediaRendition // write embedded stream to file void outputToFile(FILE *); - MediaRendition *copy() const; + std::unique_ptr copy() const; private: bool ok; @@ -152,13 +152,13 @@ class POPPLER_PRIVATE_EXPORT MediaRendition bool isEmbedded; - GooString *contentType; + std::unique_ptr contentType; // if it's embedded Object embeddedStreamObject; // if it's not embedded - GooString *fileName; + std::unique_ptr fileName; }; #endif /* _RENDITION_H_ */ diff --git a/poppler/SecurityHandler.cc b/poppler/SecurityHandler.cc index 056ba3b..cd2a8f1 100644 --- a/poppler/SecurityHandler.cc +++ b/poppler/SecurityHandler.cc @@ -13,10 +13,11 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2010, 2012, 2015, 2017, 2018, 2020-2022 Albert Astals Cid +// Copyright (C) 2010, 2012, 2015, 2017, 2018, 2020-2022, 2024 Albert Astals Cid // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2014 Fabio D'Urso // Copyright (C) 2016 Alok Anand +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -25,7 +26,7 @@ #include -#include "GooString.h" +#include "goo/GooString.h" #include "PDFDoc.h" #include "Decrypt.h" #include "Error.h" @@ -60,7 +61,7 @@ SecurityHandler::SecurityHandler(PDFDoc *docA) doc = docA; } -SecurityHandler::~SecurityHandler() { } +SecurityHandler::~SecurityHandler() = default; bool SecurityHandler::checkEncryption(const std::optional &ownerPassword, const std::optional &userPassword) { @@ -92,37 +93,20 @@ bool SecurityHandler::checkEncryption(const std::optional &ownerPassw class StandardAuthData { public: - StandardAuthData(GooString *ownerPasswordA, GooString *userPasswordA) - { - ownerPassword = ownerPasswordA; - userPassword = userPasswordA; - } + StandardAuthData(std::unique_ptr &&ownerPasswordA, std::unique_ptr &&userPasswordA) : ownerPassword(std::move(ownerPasswordA)), userPassword(std::move(userPasswordA)) { } - ~StandardAuthData() - { - if (ownerPassword) { - delete ownerPassword; - } - if (userPassword) { - delete userPassword; - } - } + ~StandardAuthData() = default; StandardAuthData(const StandardAuthData &) = delete; StandardAuthData &operator=(const StandardAuthData &) = delete; - GooString *ownerPassword; - GooString *userPassword; + const std::unique_ptr ownerPassword; + const std::unique_ptr userPassword; }; StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA) : SecurityHandler(docA) { ok = false; - fileID = nullptr; - ownerKey = nullptr; - userKey = nullptr; - ownerEnc = nullptr; - userEnc = nullptr; fileKeyLength = 0; encAlgorithm = cryptNone; @@ -217,17 +201,17 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDi if (fileIDObj1.isString()) { fileID = fileIDObj1.getString()->copy(); } else { - fileID = new GooString(); + fileID = std::make_unique(); } } else { - fileID = new GooString(); + fileID = std::make_unique(); } if (fileKeyLength > 16 || fileKeyLength < 0) { fileKeyLength = 16; } ok = true; } else if (encVersion == 5 && (encRevision == 5 || encRevision == 6)) { - fileID = new GooString(); // unused for V=R=5 + fileID = std::make_unique(); // unused for V=R=5 if (ownerEncObj.isString() && userEncObj.isString()) { ownerEnc = ownerEncObj.getString()->copy(); userEnc = userEncObj.getString()->copy(); @@ -263,24 +247,7 @@ StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA, Object *encryptDi } } -StandardSecurityHandler::~StandardSecurityHandler() -{ - if (fileID) { - delete fileID; - } - if (ownerKey) { - delete ownerKey; - } - if (userKey) { - delete userKey; - } - if (ownerEnc) { - delete ownerEnc; - } - if (userEnc) { - delete userEnc; - } -} +StandardSecurityHandler::~StandardSecurityHandler() = default; bool StandardSecurityHandler::isUnencrypted() const { @@ -292,7 +259,7 @@ bool StandardSecurityHandler::isUnencrypted() const void *StandardSecurityHandler::makeAuthData(const std::optional &ownerPassword, const std::optional &userPassword) { - return new StandardAuthData(ownerPassword ? ownerPassword->copy() : nullptr, userPassword ? userPassword->copy() : nullptr); + return new StandardAuthData(ownerPassword ? ownerPassword->copy() : std::make_unique(), userPassword ? userPassword->copy() : std::make_unique()); } void StandardSecurityHandler::freeAuthData(void *authData) @@ -308,13 +275,13 @@ bool StandardSecurityHandler::authorize(void *authData) return false; } if (authData) { - ownerPassword = ((StandardAuthData *)authData)->ownerPassword; - userPassword = ((StandardAuthData *)authData)->userPassword; + ownerPassword = ((StandardAuthData *)authData)->ownerPassword.get(); + userPassword = ((StandardAuthData *)authData)->userPassword.get(); } else { ownerPassword = nullptr; userPassword = nullptr; } - if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, ownerKey, userKey, ownerEnc, userEnc, permFlags, fileID, ownerPassword, userPassword, fileKey, encryptMetadata, &ownerPasswordOk)) { + if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength, ownerKey.get(), userKey.get(), ownerEnc.get(), userEnc.get(), permFlags, fileID.get(), ownerPassword, userPassword, fileKey, encryptMetadata, &ownerPasswordOk)) { return false; } return true; diff --git a/poppler/SecurityHandler.h b/poppler/SecurityHandler.h index de10f23..dc6906e 100644 --- a/poppler/SecurityHandler.h +++ b/poppler/SecurityHandler.h @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2012, 2018, 2020-2022 Albert Astals Cid +// Copyright (C) 2012, 2018, 2020-2022, 2024 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -122,9 +122,9 @@ class StandardSecurityHandler : public SecurityHandler bool encryptMetadata; CryptAlgorithm encAlgorithm; - GooString *ownerKey, *userKey; - GooString *ownerEnc, *userEnc; - GooString *fileID; + std::unique_ptr ownerKey, userKey; + std::unique_ptr ownerEnc, userEnc; + std::unique_ptr fileID; bool ok; }; diff --git a/poppler/SignatureHandler.h b/poppler/SignatureHandler.h deleted file mode 100644 index 77d5cca..0000000 --- a/poppler/SignatureHandler.h +++ /dev/null @@ -1,114 +0,0 @@ -//======================================================================== -// -// SignatureHandler.h -// -// This file is licensed under the GPLv2 or later -// -// Copyright 2015 André Guerreiro -// Copyright 2015 André Esser -// Copyright 2015, 2017, 2019, 2021 Albert Astals Cid -// Copyright 2017 Hans-Ulrich Jüttner -// Copyright 2018 Chinmoy Ranjan Pradhan -// Copyright 2018 Oliver Sander -// Copyright 2020 Thorsten Behrens -// Copyright 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden -// Copyright 2021 Theofilos Intzoglou -// Copyright 2021 Marek Kasik -// -//======================================================================== - -#ifndef SIGNATURE_HANDLER_H -#define SIGNATURE_HANDLER_H - -#include "goo/GooString.h" -#include "SignatureInfo.h" -#include "CertificateInfo.h" -#include "poppler_private_export.h" - -#include -#include - -/* NSPR Headers */ -#include - -/* NSS headers */ -#include -#include -#include -#include -#include -#include -#include -#include - -class POPPLER_PRIVATE_EXPORT SignatureHandler -{ -public: - explicit SignatureHandler(); - SignatureHandler(unsigned char *p7, int p7_length); - SignatureHandler(const char *certNickname, SECOidTag digestAlgTag); - ~SignatureHandler(); - time_t getSigningTime(); - std::string getSignerName(); - const char *getSignerSubjectDN(); - HASH_HashType getHashAlgorithm(); - void setSignature(unsigned char *, int); - void updateHash(unsigned char *data_block, int data_len); - void restartHash(); - SignatureValidationStatus validateSignature(); - // Use -1 as validation_time for now - CertificateValidationStatus validateCertificate(time_t validation_time, bool ocspRevocationCheck, bool useAIACertFetch); - std::unique_ptr getCertificateInfo() const; - static std::vector> getAvailableSigningCertificates(); - std::unique_ptr signDetached(const char *password) const; - - static SECOidTag getHashOidTag(const char *digestName); - - // Initializes the NSS dir with the custom given directory - // calling it with an empty string means use the default firefox db, /etc/pki/nssdb, ~/.pki/nssdb - // If you don't want a custom NSS dir and the default entries are fine for you, not calling this function is fine - // If wanted, this has to be called before doing signature validation calls - static void setNSSDir(const GooString &nssDir); - - // Gets the currently in use NSS dir - static std::string getNSSDir(); - - static void setNSSPasswordCallback(const std::function &f); - -private: - typedef struct - { - enum - { - PW_NONE = 0, - PW_FROMFILE = 1, - PW_PLAINTEXT = 2, - PW_EXTERNAL = 3 - } source; - const char *data; - } PWData; - - SignatureHandler(const SignatureHandler &); - SignatureHandler &operator=(const SignatureHandler &); - - unsigned int digestLength(SECOidTag digestAlgId); - NSSCMSMessage *CMS_MessageCreate(SECItem *cms_item); - NSSCMSSignedData *CMS_SignedDataCreate(NSSCMSMessage *cms_msg); - NSSCMSSignerInfo *CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data); - HASHContext *initHashContext(); - static void outputCallback(void *arg, const char *buf, unsigned long len); - - unsigned int hash_length; - SECOidTag digest_alg_tag; - SECItem CMSitem; - HASHContext *hash_context; - NSSCMSMessage *CMSMessage; - NSSCMSSignedData *CMSSignedData; - NSSCMSSignerInfo *CMSSignerInfo; - CERTCertificate *signing_cert; - CERTCertificate **temp_certs; - - static std::string sNssDir; -}; - -#endif diff --git a/poppler/SignatureInfo.cc b/poppler/SignatureInfo.cc index 923860b..14e9c88 100644 --- a/poppler/SignatureInfo.cc +++ b/poppler/SignatureInfo.cc @@ -13,6 +13,7 @@ // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 André Guerreiro // Copyright 2021 Marek Kasik +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -24,43 +25,10 @@ #include #include -#ifdef ENABLE_NSS3 -# include -#else -static const int HASH_AlgNULL = -1; -#endif - /* Constructor & Destructor */ -SignatureInfo::SignatureInfo() -{ - sig_status = SIGNATURE_NOT_VERIFIED; - cert_status = CERTIFICATE_NOT_VERIFIED; - cert_info = nullptr; - signer_name = nullptr; - subject_dn = nullptr; - hash_type = HASH_AlgNULL; - signing_time = 0; - sig_subfilter_supported = false; -} - -SignatureInfo::SignatureInfo(SignatureValidationStatus sig_val_status, CertificateValidationStatus cert_val_status) -{ - sig_status = sig_val_status; - cert_status = cert_val_status; - cert_info = nullptr; - signer_name = nullptr; - subject_dn = nullptr; - hash_type = HASH_AlgNULL; - signing_time = 0; - sig_subfilter_supported = false; -} - -SignatureInfo::~SignatureInfo() -{ - free(signer_name); - free(subject_dn); -} +SignatureInfo::~SignatureInfo() = default; +SignatureInfo::SignatureInfo() = default; /* GETTERS */ @@ -69,17 +37,12 @@ SignatureValidationStatus SignatureInfo::getSignatureValStatus() const return sig_status; } -CertificateValidationStatus SignatureInfo::getCertificateValStatus() const -{ - return cert_status; -} - -const char *SignatureInfo::getSignerName() const +std::string SignatureInfo::getSignerName() const { return signer_name; } -const char *SignatureInfo::getSubjectDN() const +std::string SignatureInfo::getSubjectDN() const { return subject_dn; } @@ -94,7 +57,7 @@ const GooString &SignatureInfo::getReason() const return reason; } -int SignatureInfo::getHashAlgorithm() const +HashAlgorithm SignatureInfo::getHashAlgorithm() const { return hash_type; } @@ -116,34 +79,27 @@ void SignatureInfo::setSignatureValStatus(enum SignatureValidationStatus sig_val sig_status = sig_val_status; } -void SignatureInfo::setCertificateValStatus(enum CertificateValidationStatus cert_val_status) -{ - cert_status = cert_val_status; -} - -void SignatureInfo::setSignerName(const char *signerName) +void SignatureInfo::setSignerName(const std::string &signerName) { - free(signer_name); - signer_name = signerName ? strdup(signerName) : nullptr; + signer_name = signerName; } -void SignatureInfo::setSubjectDN(const char *subjectDN) +void SignatureInfo::setSubjectDN(const std::string &subjectDN) { - free(subject_dn); - subject_dn = subjectDN ? strdup(subjectDN) : nullptr; + subject_dn = subjectDN; } void SignatureInfo::setLocation(const GooString *loc) { - location = GooString(loc->toStr()); + location = GooString(loc); } void SignatureInfo::setReason(const GooString *signingReason) { - reason = GooString(signingReason->toStr()); + reason = GooString(signingReason); } -void SignatureInfo::setHashAlgorithm(int type) +void SignatureInfo::setHashAlgorithm(HashAlgorithm type) { hash_type = type; } diff --git a/poppler/SignatureInfo.h b/poppler/SignatureInfo.h index eb1f007..8e5ad37 100644 --- a/poppler/SignatureInfo.h +++ b/poppler/SignatureInfo.h @@ -13,6 +13,7 @@ // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 André Guerreiro // Copyright 2021 Marek Kasik +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -24,6 +25,7 @@ #include "poppler_private_export.h" #include "goo/GooString.h" +#include "HashAlgorithm.h" enum SignatureValidationStatus { @@ -53,7 +55,6 @@ class POPPLER_PRIVATE_EXPORT SignatureInfo { public: SignatureInfo(); - SignatureInfo(SignatureValidationStatus, CertificateValidationStatus); ~SignatureInfo(); SignatureInfo(const SignatureInfo &) = delete; @@ -61,39 +62,36 @@ class POPPLER_PRIVATE_EXPORT SignatureInfo /* GETTERS */ SignatureValidationStatus getSignatureValStatus() const; - CertificateValidationStatus getCertificateValStatus() const; - const char *getSignerName() const; - const char *getSubjectDN() const; + std::string getSignerName() const; + std::string getSubjectDN() const; const GooString &getLocation() const; const GooString &getReason() const; - int getHashAlgorithm() const; // Returns a NSS3 HASH_HashType or -1 if compiled without NSS3 + HashAlgorithm getHashAlgorithm() const; // Returns the used HashAlgorithm, and unknown if compiled without signature support time_t getSigningTime() const; bool isSubfilterSupported() const { return sig_subfilter_supported; } const X509CertificateInfo *getCertificateInfo() const; /* SETTERS */ void setSignatureValStatus(enum SignatureValidationStatus); - void setCertificateValStatus(enum CertificateValidationStatus); - void setSignerName(const char *); - void setSubjectDN(const char *); + void setSignerName(const std::string &); + void setSubjectDN(const std::string &); void setLocation(const GooString *); void setReason(const GooString *); - void setHashAlgorithm(int); + void setHashAlgorithm(HashAlgorithm); void setSigningTime(time_t); void setSubFilterSupport(bool isSupported) { sig_subfilter_supported = isSupported; } void setCertificateInfo(std::unique_ptr); private: - SignatureValidationStatus sig_status; - CertificateValidationStatus cert_status; + SignatureValidationStatus sig_status = SIGNATURE_NOT_VERIFIED; std::unique_ptr cert_info; - char *signer_name; - char *subject_dn; + std::string signer_name; + std::string subject_dn; GooString location; GooString reason; - int hash_type; - time_t signing_time; - bool sig_subfilter_supported; + HashAlgorithm hash_type = HashAlgorithm::Unknown; + time_t signing_time = 0; + bool sig_subfilter_supported = false; }; #endif diff --git a/poppler/Sound.cc b/poppler/Sound.cc index 716e44c..3cecb06 100644 --- a/poppler/Sound.cc +++ b/poppler/Sound.cc @@ -101,7 +101,7 @@ Sound::Sound(const Object *obj, bool readAttrs) } } -Sound::~Sound() { } +Sound::~Sound() = default; Stream *Sound::getStream() { diff --git a/poppler/Sound.h b/poppler/Sound.h index aa080bf..df52f29 100644 --- a/poppler/Sound.h +++ b/poppler/Sound.h @@ -1,6 +1,6 @@ /* Sound.h - an object that holds the sound structure * Copyright (C) 2006-2007, Pino Toscano - * Copyright (C) 2017-2021, Albert Astals Cid + * Copyright (C) 2017-2021, 2024, Albert Astals Cid * Copyright (C) 2020, Oliver Sander * * This program is free software; you can redistribute it and/or modify @@ -23,8 +23,9 @@ #include -class Object; -class Stream; +#include "poppler_private_export.h" + +#include "Object.h" //------------------------------------------------------------------------ diff --git a/poppler/SplashOutputDev.cc b/poppler/SplashOutputDev.cc index 6ba9842..e26e362 100644 --- a/poppler/SplashOutputDev.cc +++ b/poppler/SplashOutputDev.cc @@ -15,12 +15,12 @@ // // Copyright (C) 2005 Takashi Iwai // Copyright (C) 2006 Stefan Schweizer -// Copyright (C) 2006-2022 Albert Astals Cid +// Copyright (C) 2006-2022, 2024 Albert Astals Cid // Copyright (C) 2006 Krzysztof Kowalczyk // Copyright (C) 2006 Scott Turner // Copyright (C) 2007 Koji Otani // Copyright (C) 2009 Petr Gajdos -// Copyright (C) 2009-2016, 2020 Thomas Freitag +// Copyright (C) 2009-2016, 2020, 2022, 2023 Thomas Freitag // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009, 2014-2016, 2019 William Bader // Copyright (C) 2010 Patrick Spendrin @@ -37,12 +37,13 @@ // Copyright (C) 2015 Tamas Szekeres // Copyright (C) 2015 Kenji Uno // Copyright (C) 2016 Takahiro Hashimoto -// Copyright (C) 2017, 2021 Even Rouault +// Copyright (C) 2017, 2021, 2024 Even Rouault // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018, 2019 Stefan Brüns // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Christian Persch // Copyright (C) 2020-2022 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -54,6 +55,7 @@ #include #include #include +#include "Stream.h" #include "goo/gfile.h" #include "GlobalParams.h" #include "Error.h" @@ -183,7 +185,7 @@ SplashGouraudPattern::SplashGouraudPattern(bool bDirectColorTranslationA, GfxSta gfxMode = shadingA->getColorSpace()->getMode(); } -SplashGouraudPattern::~SplashGouraudPattern() { } +SplashGouraudPattern::~SplashGouraudPattern() = default; void SplashGouraudPattern::getNonParametrizedTriangle(int i, SplashColorMode mode, double *x0, double *y0, SplashColorPtr color0, double *x1, double *y1, SplashColorPtr color1, double *x2, double *y2, SplashColorPtr color2) { @@ -248,7 +250,7 @@ SplashFunctionPattern::SplashFunctionPattern(SplashColorMode colorModeA, GfxStat convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } -SplashFunctionPattern::~SplashFunctionPattern() { } +SplashFunctionPattern::~SplashFunctionPattern() = default; bool SplashFunctionPattern::getColor(int x, int y, SplashColorPtr c) { @@ -290,7 +292,7 @@ SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, Gfx gfxMode = shadingA->getColorSpace()->getMode(); } -SplashUnivariatePattern::~SplashUnivariatePattern() { } +SplashUnivariatePattern::~SplashUnivariatePattern() = default; bool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) { @@ -345,7 +347,7 @@ SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *s convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } -SplashRadialPattern::~SplashRadialPattern() { } +SplashRadialPattern::~SplashRadialPattern() = default; bool SplashRadialPattern::getParameter(double xs, double ys, double *t) { @@ -457,7 +459,7 @@ SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *sta convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor); } -SplashAxialPattern::~SplashAxialPattern() { } +SplashAxialPattern::~SplashAxialPattern() = default; bool SplashAxialPattern::getParameter(double xc, double yc, double *t) { @@ -796,51 +798,34 @@ static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest, Spl static int getLum(int r, int g, int b) { - return (int)(0.3 * r + 0.59 * g + 0.11 * b); + // (int)(0.3 * r + 0.59 * g + 0.11 * b) = + // (int)(256 / 256 * 0.3 * r + 256 / 256 * 0.59 * g + 256 / 256 * 0.11 * b) + // (int)((77 * r + 151 * g + 28 * b) / 256) = // round! + return (int)((r * 77 + g * 151 + b * 28 + 0x80) >> 8); } static int getSat(int r, int g, int b) { - int rgbMin, rgbMax; + int rgbMin = std::min({ r, g, b }); + int rgbMax = std::max({ r, g, b }); - rgbMin = rgbMax = r; - if (g < rgbMin) { - rgbMin = g; - } else if (g > rgbMax) { - rgbMax = g; - } - if (b < rgbMin) { - rgbMin = b; - } else if (b > rgbMax) { - rgbMax = b; - } return rgbMax - rgbMin; } static void clipColor(int rIn, int gIn, int bIn, unsigned char *rOut, unsigned char *gOut, unsigned char *bOut) { - int lum, rgbMin, rgbMax; + int lum = getLum(rIn, gIn, bIn); + int rgbMin = std::min({ rIn, bIn, gIn }); + int rgbMax = std::max({ rIn, bIn, gIn }); - lum = getLum(rIn, gIn, bIn); - rgbMin = rgbMax = rIn; - if (gIn < rgbMin) { - rgbMin = gIn; - } else if (gIn > rgbMax) { - rgbMax = gIn; - } - if (bIn < rgbMin) { - rgbMin = bIn; - } else if (bIn > rgbMax) { - rgbMax = bIn; - } if (rgbMin < 0) { - *rOut = (unsigned char)(lum + ((rIn - lum) * lum) / (lum - rgbMin)); - *gOut = (unsigned char)(lum + ((gIn - lum) * lum) / (lum - rgbMin)); - *bOut = (unsigned char)(lum + ((bIn - lum) * lum) / (lum - rgbMin)); + *rOut = (unsigned char)std::clamp(lum + ((rIn - lum) * lum) / (lum - rgbMin), 0, 255); + *gOut = (unsigned char)std::clamp(lum + ((gIn - lum) * lum) / (lum - rgbMin), 0, 255); + *bOut = (unsigned char)std::clamp(lum + ((bIn - lum) * lum) / (lum - rgbMin), 0, 255); } else if (rgbMax > 255) { - *rOut = (unsigned char)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum)); - *gOut = (unsigned char)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum)); - *bOut = (unsigned char)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum)); + *rOut = (unsigned char)std::clamp(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum), 0, 255); + *gOut = (unsigned char)std::clamp(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum), 0, 255); + *bOut = (unsigned char)std::clamp(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum), 0, 255); } else { *rOut = rIn; *gOut = gIn; @@ -889,8 +874,8 @@ static void setSat(unsigned char rIn, unsigned char gIn, unsigned char bIn, int minOut = bOut; } if (rgbMax > rgbMin) { - *midOut = (unsigned char)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin); - *maxOut = (unsigned char)sat; + *midOut = (unsigned char)std::clamp(((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin), 0, 255); + *maxOut = (unsigned char)std::clamp(sat, 0, 255); } else { *midOut = *maxOut = 0; } @@ -1085,7 +1070,7 @@ class SplashOutFontFileID : public SplashFontFileID ~SplashOutFontFileID() override; - bool matches(SplashFontFileID *id) override { return ((SplashOutFontFileID *)id)->r == r; } + bool matches(const SplashFontFileID &id) const override { return static_cast(id).r == r; } private: Ref r; @@ -1303,15 +1288,9 @@ SplashOutputDev::~SplashOutputDev() for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; } - if (fontEngine) { - delete fontEngine; - } - if (splash) { - delete splash; - } - if (bitmap) { - delete bitmap; - } + delete fontEngine; + delete splash; + delete bitmap; delete textClipPath; } @@ -1320,9 +1299,7 @@ void SplashOutputDev::startDoc(PDFDoc *docA) int i; doc = docA; - if (fontEngine) { - delete fontEngine; - } + delete fontEngine; fontEngine = new SplashFontEngine(enableFreeType, enableFreeTypeHinting, enableSlightHinting, getFontAntialias() && colorMode != splashModeMono1); for (i = 0; i < nT3Fonts; ++i) { delete t3FontCache[i]; @@ -1721,7 +1698,7 @@ void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace, bool overprint mask &= ~8; } } - if (grayIndexed) { + if (grayIndexed && colorSpace->getMode() != csDeviceN) { mask &= ~7; } else if (colorSpace->getMode() == csSeparation) { GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace; @@ -1838,12 +1815,11 @@ void SplashOutputDev::updateFont(GfxState * /*state*/) void SplashOutputDev::doUpdateFont(GfxState *state) { GfxFontType fontType; - SplashOutFontFileID *id = nullptr; SplashFontFile *fontFile; + std::unique_ptr id; SplashFontSrc *fontsrc = nullptr; const double *textMat; double m11, m12, m21, m22, fontSize; - int faceIndex = 0; SplashCoord mat[4]; bool recreateFont = false; bool doAdjustFontMatrix = false; @@ -1853,37 +1829,35 @@ void SplashOutputDev::doUpdateFont(GfxState *state) GfxFont *const gfxFont = state->getFont().get(); if (!gfxFont) { - goto err1; + goto err; } fontType = gfxFont->getType(); if (fontType == fontType3) { - goto err1; + goto err; } // sanity-check the font size - skip anything larger than 10 inches // (this avoids problems allocating memory for the font cache) if (state->getTransformedFontSize() > 10 * (state->getHDPI() + state->getVDPI())) { - goto err1; + goto err; } // check the font file cache reload: - delete id; if (fontsrc && !fontsrc->isFile) { fontsrc->unref(); fontsrc = nullptr; } - id = new SplashOutFontFileID(gfxFont->getID()); - if ((fontFile = fontEngine->getFontFile(id))) { - delete id; - + id = std::make_unique(gfxFont->getID()); + if ((fontFile = fontEngine->getFontFile(*id))) { + id.reset(); } else { std::optional fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), nullptr); if (!fontLoc) { error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); - goto err2; + goto err; } // embedded font @@ -1894,7 +1868,7 @@ void SplashOutputDev::doUpdateFont(GfxState *state) // if there is an embedded font, read it to memory tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef()); if (!tmpBuf) { - goto err2; + goto err; } // external font @@ -1914,42 +1888,41 @@ void SplashOutputDev::doUpdateFont(GfxState *state) // load the font file switch (fontType) { case fontType1: - if (!(fontFile = fontEngine->loadType1Font(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + if (!(fontFile = fontEngine->loadType1Font(std::move(id), fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding(), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; case fontType1C: - if (!(fontFile = fontEngine->loadType1CFont(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + if (!(fontFile = fontEngine->loadType1CFont(std::move(id), fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding(), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; case fontType1COT: - if (!(fontFile = fontEngine->loadOpenTypeT1CFont(id, fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) { + if (!(fontFile = fontEngine->loadOpenTypeT1CFont(std::move(id), fontsrc, (const char **)((Gfx8BitFont *)gfxFont)->getEncoding(), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; case fontTrueType: case fontTrueTypeOT: { std::unique_ptr ff; if (!fileName.empty()) { - ff = FoFiTrueType::load(fileName.c_str()); + ff = FoFiTrueType::load(fileName.c_str(), fontLoc->fontNum); } else { - ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size()); + ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size(), fontLoc->fontNum); } - int *codeToGID; - const int n = ff ? 256 : 0; + std::vector codeToGID; if (ff) { codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff.get()); // if we're substituting for a non-TrueType font, we need to mark @@ -1962,84 +1935,71 @@ void SplashOutputDev::doUpdateFont(GfxState *state) } } } - } else { - codeToGID = nullptr; } - if (!(fontFile = fontEngine->loadTrueTypeFont(id, fontsrc, codeToGID, n))) { + if (!(fontFile = fontEngine->loadTrueTypeFont(std::move(id), fontsrc, std::move(codeToGID), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; } case fontCIDType0: case fontCIDType0C: - if (!(fontFile = fontEngine->loadCIDFont(id, fontsrc))) { + if (!(fontFile = fontEngine->loadCIDFont(std::move(id), fontsrc, fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; case fontCIDType0COT: { - int *codeToGID; - int n; - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); + std::vector codeToGID; + if (((GfxCIDFont *)gfxFont)->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID(); } else { - codeToGID = nullptr; - n = 0; } - if (!(fontFile = fontEngine->loadOpenTypeCFFFont(id, fontsrc, codeToGID, n))) { + if (!(fontFile = fontEngine->loadOpenTypeCFFFont(std::move(id), fontsrc, std::move(codeToGID), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); - gfree(codeToGID); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; } case fontCIDType2: case fontCIDType2OT: { - int *codeToGID = nullptr; - int n = 0; - if (((GfxCIDFont *)gfxFont)->getCIDToGID()) { - n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen(); - if (n) { - codeToGID = (int *)gmallocn(n, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(), n * sizeof(int)); - } + std::vector codeToGID; + if (((GfxCIDFont *)gfxFont)->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont)->getCIDToGID(); } else { std::unique_ptr ff; if (!fileName.empty()) { - ff = FoFiTrueType::load(fileName.c_str()); + ff = FoFiTrueType::load(fileName.c_str(), fontLoc->fontNum); } else { - ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size()); + ff = FoFiTrueType::make(fontsrc->buf.data(), fontsrc->buf.size(), fontLoc->fontNum); } if (!ff) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); - goto err2; + goto err; } - codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff.get(), &n); + codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff.get()); } - if (!(fontFile = fontEngine->loadTrueTypeFont(id, fontsrc, codeToGID, n, faceIndex))) { + if (!(fontFile = fontEngine->loadTrueTypeFont(std::move(id), fontsrc, std::move(codeToGID), fontLoc->fontNum))) { error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); if (gfxFont->invalidateEmbeddedFont()) { goto reload; } - goto err2; + goto err; } break; } default: // this shouldn't happen - goto err2; + goto err; } fontFile->doAdjustMatrix = doAdjustFontMatrix; } @@ -2100,13 +2060,10 @@ void SplashOutputDev::doUpdateFont(GfxState *state) } return; -err2: - delete id; -err1: +err: if (fontsrc && !fontsrc->isFile) { fontsrc->unref(); } - return; } void SplashOutputDev::stroke(GfxState *state) @@ -2285,9 +2242,7 @@ void SplashOutputDev::drawChar(GfxState *state, double x, double y, double dx, d splash->setStrokeAdjust(strokeAdjust); } - if (path) { - delete path; - } + delete path; } bool SplashOutputDev::beginType3Char(GfxState *state, double x, double y, double dx, double dy, CharCode code, const Unicode *u, int uLen) @@ -2619,7 +2574,7 @@ void SplashOutputDev::endTextObject(GfxState *state) struct SplashOutImageMaskData { - ImageStream *imgStr; + std::unique_ptr imgStr; bool invert; int width, height, y; }; @@ -2667,8 +2622,10 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, i mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); + imgMaskData.imgStr = std::make_unique(str, width, 1, 1); + if (!imgMaskData.imgStr->reset()) { + return; + } imgMaskData.invert = invert ? false : true; imgMaskData.width = width; imgMaskData.height = height; @@ -2677,12 +2634,13 @@ void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, i splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); if (inlineImg) { while (imgMaskData.y < height) { - imgMaskData.imgStr->getLine(); + if (!imgMaskData.imgStr->getLine()) { + break; + } ++imgMaskData.y; } } - delete imgMaskData.imgStr; str->close(); } @@ -2705,6 +2663,10 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Str return; } } + imgMaskData.imgStr = std::make_unique(str, width, 1, 1); + if (!imgMaskData.imgStr->reset()) { + return; + } beginTransparencyGroup(state, bbox, nullptr, false, false, false); baseMatrix[4] -= transpGroupStack->tx; @@ -2717,8 +2679,6 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Str mat[3] = -ctm[3]; mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; - imgMaskData.imgStr = new ImageStream(str, width, 1, 1); - imgMaskData.imgStr->reset(); imgMaskData.invert = invert ? false : true; imgMaskData.width = width; imgMaskData.height = height; @@ -2732,7 +2692,6 @@ void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state, Object *ref, Str maskSplash->setFillPattern(new SplashSolidColor(maskColor)); maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != nullptr); delete maskSplash; - delete imgMaskData.imgStr; str->close(); } @@ -2764,13 +2723,13 @@ void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMa struct SplashOutImageData { - ImageStream *imgStr; + std::unique_ptr imgStr; GfxImageColorMap *colorMap; SplashColorPtr lookup; const int *maskColors; SplashColorMode colorMode; int width, height, y; - ImageStream *maskStr; + std::unique_ptr maskStr; GfxImageColorMap *maskColorMap; SplashColor matteColor; }; @@ -2957,7 +2916,7 @@ bool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine, unsigned ch } } - if (imgData->maskStr != nullptr && (p = imgData->maskStr->getLine()) != nullptr) { + if (imgData->maskStr && (p = imgData->maskStr->getLine()) != nullptr) { int destComps = splashColorModeNComps[imgData->colorMode]; int convComps = (imgData->colorMode == splashModeXBGR8) ? 3 : destComps; imgData->maskColorMap->getGrayLine(p, p, imgData->width); @@ -3286,6 +3245,11 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int w return; } } + imgData.imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgData.imgStr->reset()) { + return; + } + mat[0] = ctm[0]; mat[1] = ctm[1]; mat[2] = -ctm[2]; @@ -3293,14 +3257,11 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int w mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; - imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgData.imgStr->reset(); imgData.colorMap = colorMap; imgData.maskColors = maskColors; imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; - imgData.maskStr = nullptr; imgData.maskColorMap = nullptr; imgData.y = 0; @@ -3408,15 +3369,14 @@ void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int w } gfree(imgData.lookup); - delete imgData.imgStr; str->close(); } struct SplashOutMaskedImageData { - ImageStream *imgStr; + std::unique_ptr imgStr; GfxImageColorMap *colorMap; - SplashBitmap *mask; + std::unique_ptr mask; SplashColorPtr lookup; SplashColorMode colorMode; int width, height, y; @@ -3530,13 +3490,10 @@ bool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine, unsig void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate) { - GfxImageColorMap *maskColorMap; SplashCoord mat[6]; SplashOutMaskedImageData imgData; SplashOutImageMaskData imgMaskData; SplashColorMode srcMode; - SplashBitmap *maskBitmap; - Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; @@ -3554,9 +3511,8 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, Object maskDecode(new Array((xref) ? xref : doc->getXRef())); maskDecode.arrayAdd(Object(maskInvert ? 0 : 1)); maskDecode.arrayAdd(Object(maskInvert ? 1 : 0)); - maskColorMap = new GfxImageColorMap(1, &maskDecode, new GfxDeviceGrayColorSpace()); - drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate); - delete maskColorMap; + GfxImageColorMap maskColorMap(1, &maskDecode, std::make_unique()); + drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate, maskStr, maskWidth, maskHeight, &maskColorMap, maskInterpolate); } else { //----- scale the mask image to the same size as the source image @@ -3567,34 +3523,34 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, mat[3] = (SplashCoord)height; mat[4] = 0; mat[5] = 0; - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1); - imgMaskData.imgStr->reset(); + imgMaskData.imgStr = std::make_unique(maskStr, maskWidth, 1, 1); + if (!imgMaskData.imgStr->reset()) { + return; + } imgMaskData.invert = maskInvert ? false : true; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); + auto maskBitmap = std::make_unique(width, height, 1, splashModeMono1, false); if (!maskBitmap->getDataPtr()) { - delete maskBitmap; width = height = 1; - maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, false); + maskBitmap = std::make_unique(width, height, 1, splashModeMono1, false); + } + { + Splash maskSplash { maskBitmap.get(), false }; + maskColor[0] = 0; + maskSplash.clear(maskColor); + maskColor[0] = 0xff; + maskSplash.setFillPattern(new SplashSolidColor(maskColor)); + maskSplash.fillImageMask(&imageMaskSrc, &imgMaskData, maskWidth, maskHeight, mat, false); + maskStr->close(); } - maskSplash = new Splash(maskBitmap, false); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskColor[0] = 0xff; - maskSplash->setFillPattern(new SplashSolidColor(maskColor)); - maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, maskWidth, maskHeight, mat, false); - delete imgMaskData.imgStr; - maskStr->close(); - delete maskSplash; //----- draw the source image const double *ctm = state->getCTM(); for (i = 0; i < 6; ++i) { if (!std::isfinite(ctm[i])) { - delete maskBitmap; return; } } @@ -3605,10 +3561,12 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, mat[4] = ctm[2] + ctm[4]; mat[5] = ctm[3] + ctm[5]; - imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgData.imgStr->reset(); + imgData.imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgData.imgStr->reset()) { + return; + } imgData.colorMap = colorMap; - imgData.mask = maskBitmap; + imgData.mask = std::move(maskBitmap); imgData.colorMode = colorMode; imgData.width = width; imgData.height = height; @@ -3681,9 +3639,7 @@ void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, srcMode = colorMode; } splash->drawImage(&maskedImageSrc, nullptr, &imgData, srcMode, true, width, height, mat, interpolate); - delete maskBitmap; gfree(imgData.lookup); - delete imgData.imgStr; str->close(); } } @@ -3696,13 +3652,13 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, S SplashOutImageData imgMaskData; SplashColorMode srcMode; SplashBitmap *maskBitmap; - Splash *maskSplash; SplashColor maskColor; GfxGray gray; GfxRGB rgb; GfxCMYK cmyk; GfxColor deviceN; unsigned char pix; + std::unique_ptr ownedMaskStr; colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS); setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(), state->getOverprintMode(), nullptr); @@ -3727,24 +3683,29 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, S if (checkedMultiply(maskWidth, maskHeight, &maskChars)) { return; } + if (!maskStr->reset()) { + return; + } unsigned char *data = (unsigned char *)gmalloc(maskChars); - maskStr->reset(); const int readChars = maskStr->doGetChars(maskChars, data); if (unlikely(readChars < maskChars)) { memset(&data[readChars], 0, maskChars - readChars); } maskStr->close(); - maskStr = new AutoFreeMemStream((char *)data, 0, maskChars, maskStr->getDictObject()->copy()); + + ownedMaskStr = std::make_unique((char *)data, 0, maskChars, maskStr->getDictObject()->copy()); + maskStr = ownedMaskStr.get(); + } + imgMaskData.imgStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); + if (!imgMaskData.imgStr->reset()) { + return; } - imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); - imgMaskData.imgStr->reset(); imgMaskData.colorMap = maskColorMap; imgMaskData.maskColors = nullptr; imgMaskData.colorMode = splashModeMono8; imgMaskData.width = maskWidth; imgMaskData.height = maskHeight; imgMaskData.y = 0; - imgMaskData.maskStr = nullptr; imgMaskData.maskColorMap = nullptr; const unsigned imgMaskDataLookupSize = 1 << maskColorMap->getBits(); imgMaskData.lookup = (SplashColorPtr)gmalloc(imgMaskDataLookupSize); @@ -3754,22 +3715,24 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, S imgMaskData.lookup[i] = colToByte(gray); } maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); - maskSplash = new Splash(maskBitmap, vectorAntialias); - maskColor[0] = 0; - maskSplash->clear(maskColor); - maskSplash->drawImage(&imageSrc, nullptr, &imgMaskData, splashModeMono8, false, maskWidth, maskHeight, mat, maskInterpolate); - delete imgMaskData.imgStr; - if (maskColorMap->getMatteColor() == nullptr) { - maskStr->close(); + { + Splash maskSplash { maskBitmap, vectorAntialias }; + maskColor[0] = 0; + maskSplash.clear(maskColor); + maskSplash.drawImage(&imageSrc, nullptr, &imgMaskData, splashModeMono8, false, maskWidth, maskHeight, mat, maskInterpolate); + if (maskColorMap->getMatteColor() == nullptr) { + maskStr->close(); + } + gfree(imgMaskData.lookup); } - gfree(imgMaskData.lookup); - delete maskSplash; splash->setSoftMask(maskBitmap); //----- draw the source image - imgData.imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgData.imgStr->reset(); + imgData.imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgData.imgStr->reset()) { + return; + } imgData.colorMap = colorMap; imgData.maskColors = nullptr; imgData.colorMode = colorMode; @@ -3780,8 +3743,10 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, S if (maskColorMap->getMatteColor() != nullptr) { getMatteColor(colorMode, colorMap, maskColorMap->getMatteColor(), imgData.matteColor); imgData.maskColorMap = maskColorMap; - imgData.maskStr = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); - imgData.maskStr->reset(); + imgData.maskStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); + if (!imgData.maskStr->reset()) { + return; + } } imgData.y = 0; @@ -3862,11 +3827,9 @@ void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object * /* ref */, S splash->drawImage(&imageSrc, nullptr, &imgData, srcMode, false, width, height, mat, interpolate); splash->setSoftMask(nullptr); gfree(imgData.lookup); - delete imgData.maskStr; - delete imgData.imgStr; - if (maskColorMap->getMatteColor() != nullptr) { - maskStr->close(); - delete maskStr; + if (ownedMaskStr) { + ownedMaskStr->close(); + ownedMaskStr.reset(); } str->close(); } @@ -4069,7 +4032,7 @@ void SplashOutputDev::paintTransparencyGroup(GfxState *state, const double *bbox void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alpha, Function *transferFunc, GfxColor *backdropColor) { - SplashBitmap *softMask, *tBitmap; + SplashBitmap *tBitmap; Splash *tSplash; SplashTransparencyGroup *transpGroup; SplashColor color; @@ -4131,7 +4094,11 @@ void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alph } } - softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); + SplashBitmap *softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, false); + if (!softMask->getDataPtr()) { + delete softMask; + softMask = new SplashBitmap(1, 1, 1, splashModeMono8, false); + } unsigned char fill = 0; if (transpGroupStack->blendingColorSpace) { transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray); @@ -4141,11 +4108,11 @@ void SplashOutputDev::setSoftMask(GfxState *state, const double *bbox, bool alph p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx; int xMax = tBitmap->getWidth(); int yMax = tBitmap->getHeight(); - if (xMax > bitmap->getWidth() - tx) { - xMax = bitmap->getWidth() - tx; + if (xMax > softMask->getWidth() - tx) { + xMax = softMask->getWidth() - tx; } - if (yMax > bitmap->getHeight() - ty) { - yMax = bitmap->getHeight() - ty; + if (yMax > softMask->getHeight() - ty) { + yMax = softMask->getHeight() - ty; } for (y = 0; y < yMax; ++y) { for (x = 0; x < xMax; ++x) { @@ -4326,6 +4293,15 @@ bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *cat surface_height = (int)ceil(fabs(ky)); repeatX = x1 - x0; repeatY = y1 - y0; + while ((unsigned long)repeatX * repeatY > 0x800000L) { + // try to avoid bogus memory allocation size + if (repeatX > 1) { + repeatX /= 2; + } + if (repeatY > 1) { + repeatY /= 2; + } + } } else { if ((unsigned long)surface_width * surface_height > 0x800000L) { state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]); diff --git a/poppler/SplashOutputDev.h b/poppler/SplashOutputDev.h index 0461412..88b13b4 100644 --- a/poppler/SplashOutputDev.h +++ b/poppler/SplashOutputDev.h @@ -341,20 +341,11 @@ class POPPLER_PRIVATE_EXPORT SplashOutputDev : public OutputDev void setVectorAntialias(bool vaa) override; #endif - bool getFontAntialias() - { - return fontAntialias; - } - void setFontAntialias(bool anti) - { - fontAntialias = anti; - } + bool getFontAntialias() { return fontAntialias; } + void setFontAntialias(bool anti) { fontAntialias = anti; } void setFreeTypeHinting(bool enable, bool enableSlightHinting); - void setEnableFreeType(bool enable) - { - enableFreeType = enable; - } + void setEnableFreeType(bool enable) { enableFreeType = enable; } protected: void doUpdateFont(GfxState *state); diff --git a/poppler/Stream-CCITT.h b/poppler/Stream-CCITT.h index 3029ed0..ab7adc6 100644 --- a/poppler/Stream-CCITT.h +++ b/poppler/Stream-CCITT.h @@ -31,7 +31,7 @@ struct CCITTCode short n; }; -#define ccittEOL -2 +#define ccittEOL (-2) //------------------------------------------------------------------------ // 2D codes diff --git a/poppler/Stream.cc b/poppler/Stream.cc index 0772063..0f35151 100644 --- a/poppler/Stream.cc +++ b/poppler/Stream.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Jeff Muizelaar -// Copyright (C) 2006-2010, 2012-2014, 2016-2021 Albert Astals Cid +// Copyright (C) 2006-2010, 2012-2014, 2016-2021, 2023, 2024 Albert Astals Cid // Copyright (C) 2007 Krzysztof Kowalczyk // Copyright (C) 2008 Julien Rebetez // Copyright (C) 2009 Carlos Garcia Campos @@ -26,7 +26,7 @@ // Copyright (C) 2012, 2013, 2020 Thomas Freitag // Copyright (C) 2012, 2021 Oliver Sander // Copyright (C) 2012 Fabio D'Urso -// Copyright (C) 2012 Even Rouault +// Copyright (C) 2012, 2024 Even Rouault // Copyright (C) 2013, 2017, 2018 Adrian Johnson // Copyright (C) 2013, 2018 Adam Reichold // Copyright (C) 2013 Pino Toscano @@ -41,6 +41,8 @@ // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2025 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -165,9 +167,9 @@ unsigned int Stream::discardChars(unsigned int n) return count; } -GooString *Stream::getPSFilter(int psLevel, const char *indent) +std::optional Stream::getPSFilter(int psLevel, const char *indent) { - return new GooString(); + return std::string {}; } static Stream *wrapEOFStream(Stream *str) @@ -237,12 +239,12 @@ class BaseStreamStream : public Stream ~BaseStreamStream() override; StreamKind getKind() const override { return str->getBaseStream()->getKind(); } - void reset() override { str->getBaseStream()->reset(); } + [[nodiscard]] bool reset() override { return str->getBaseStream()->reset(); } int getChar() override { return str->getBaseStream()->getChar(); } int lookChar() override { return str->getBaseStream()->lookChar(); } bool isBinary(bool last = true) const override { return str->getBaseStream()->isBinary(); } int getUnfilteredChar() override { return str->getBaseStream()->getUnfilteredChar(); } - void unfilteredReset() override { str->getBaseStream()->unfilteredReset(); } + [[nodiscard]] bool unfilteredReset() override { return str->getBaseStream()->unfilteredReset(); } Goffset getPos() override { return str->getBaseStream()->getPos(); } void setPos(Goffset pos, int dir) override { str->getBaseStream()->setPos(pos, dir); } BaseStream *getBaseStream() override { return str->getBaseStream()->getBaseStream(); } @@ -415,9 +417,9 @@ Stream *Stream::makeFilter(const char *name, Stream *str, Object *params, int re //------------------------------------------------------------------------ // OutStream //------------------------------------------------------------------------ -OutStream::OutStream() { } +OutStream::OutStream() = default; -OutStream::~OutStream() { } +OutStream::~OutStream() = default; //------------------------------------------------------------------------ // FileOutStream @@ -445,6 +447,11 @@ void FileOutStream::put(char c) fputc(c, f); } +size_t FileOutStream::write(std::span data) +{ + return fwrite(data.data(), sizeof(decltype(data)::element_type), data.size(), f); +} + void FileOutStream::printf(const char *format, ...) { va_list argptr; @@ -463,7 +470,7 @@ BaseStream::BaseStream(Object &&dictA, Goffset lengthA) length = lengthA; } -BaseStream::~BaseStream() { } +BaseStream::~BaseStream() = default; //------------------------------------------------------------------------ // BaseStream @@ -474,15 +481,17 @@ BaseSeekInputStream::BaseSeekInputStream(Goffset startA, bool limitedA, Goffset { } -BaseSeekInputStream::~BaseSeekInputStream() { } +BaseSeekInputStream::~BaseSeekInputStream() = default; -void BaseSeekInputStream::reset() +bool BaseSeekInputStream::reset() { savePos = currentPos(); setCurrentPos(start); saved = true; bufPtr = bufEnd = buf; bufPos = start; + + return true; } void BaseSeekInputStream::close() @@ -573,7 +582,7 @@ FilterStream::FilterStream(Stream *strA) str = strA; } -FilterStream::~FilterStream() { } +FilterStream::~FilterStream() = default; void FilterStream::close() { @@ -628,9 +637,9 @@ ImageStream::~ImageStream() gfree(inputLine); } -void ImageStream::reset() +bool ImageStream::reset() { - str->reset(); + return str->reset(); } void ImageStream::close() @@ -656,7 +665,7 @@ bool ImageStream::getPixel(unsigned char *pix) unsigned char *ImageStream::getLine() { - if (unlikely(inputLine == nullptr)) { + if (unlikely(inputLine == nullptr || imgLine == nullptr)) { return nullptr; } @@ -728,9 +737,10 @@ StreamPredictor::StreamPredictor(Stream *strA, int predictorA, int widthA, int n predLine = nullptr; ok = false; - nVals = width * nComps; - if (width <= 0 || nComps <= 0 || nBits <= 0 || nComps > gfxColorMaxComps || nBits > 16 || width >= INT_MAX / nComps || // check for overflow in nVals - nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes + if (checkedMultiply(width, nComps, &nVals)) { + return; + } + if (width <= 0 || nComps <= 0 || nBits <= 0 || nComps > gfxColorMaxComps || nBits > 16 || nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes return; } pixBytes = (nComps * nBits + 7) >> 3; @@ -950,13 +960,15 @@ Stream *FileStream::makeSubStream(Goffset startA, bool limitedA, Goffset lengthA return new FileStream(file, startA, limitedA, lengthA, std::move(dictA)); } -void FileStream::reset() +bool FileStream::reset() { savePos = offset; offset = start; saved = true; bufPtr = bufEnd = buf; bufPos = start; + + return true; } void FileStream::close() @@ -1051,7 +1063,7 @@ Stream *CachedFileStream::makeSubStream(Goffset startA, bool limitedA, Goffset l return new CachedFileStream(cc, startA, limitedA, lengthA, std::move(dictA)); } -void CachedFileStream::reset() +bool CachedFileStream::reset() { savePos = (unsigned int)cc->tell(); cc->seek(start, SEEK_SET); @@ -1059,6 +1071,8 @@ void CachedFileStream::reset() saved = true; bufPtr = bufEnd = buf; bufPos = start; + + return true; } void CachedFileStream::close() @@ -1165,10 +1179,11 @@ EmbedStream::~EmbedStream() } } -void EmbedStream::reset() +bool EmbedStream::reset() { + bool success = true; if (str->getPos() != start) { - str->reset(); + success = str->reset(); // Might be a FilterStream that does not support str->setPos(start) while (str->getPos() < start) { if (str->getChar() == EOF) { @@ -1177,11 +1192,14 @@ void EmbedStream::reset() } if (str->getPos() != start) { error(errInternal, -1, "Failed to reset EmbedStream"); + success = false; } } record = false; replay = false; bufPos = 0; + + return success; } BaseStream *EmbedStream::copy() @@ -1326,11 +1344,12 @@ ASCIIHexStream::~ASCIIHexStream() delete str; } -void ASCIIHexStream::reset() +bool ASCIIHexStream::reset() { - str->reset(); buf = EOF; eof = false; + + return str->reset(); } int ASCIIHexStream::lookChar() @@ -1388,17 +1407,17 @@ int ASCIIHexStream::lookChar() return buf; } -GooString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent) +std::optional ASCIIHexStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("/ASCIIHexDecode filter\n"); + s->append(indent).append("/ASCIIHexDecode filter\n"); return s; } @@ -1422,11 +1441,12 @@ ASCII85Stream::~ASCII85Stream() delete str; } -void ASCII85Stream::reset() +bool ASCII85Stream::reset() { - str->reset(); index = n = 0; eof = false; + + return str->reset(); } int ASCII85Stream::lookChar() @@ -1478,17 +1498,17 @@ int ASCII85Stream::lookChar() return b[index]; } -GooString *ASCII85Stream::getPSFilter(int psLevel, const char *indent) +std::optional ASCII85Stream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("/ASCII85Decode filter\n"); + s->append(indent).append("/ASCII85Decode filter\n"); return s; } @@ -1520,9 +1540,7 @@ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors, int b LZWStream::~LZWStream() { - if (pred) { - delete pred; - } + delete pred; delete str; } @@ -1598,12 +1616,14 @@ int LZWStream::getChars(int nChars, unsigned char *buffer) return n; } -void LZWStream::reset() +bool LZWStream::reset() { - str->reset(); + bool success = str->reset(); eof = false; inputBits = 0; clearTable(); + + return success; } bool LZWStream::processNextCode() @@ -1701,17 +1721,17 @@ int LZWStream::getCode() return code; } -GooString *LZWStream::getPSFilter(int psLevel, const char *indent) +std::optional LZWStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2 || pred) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("<< "); + s->append(indent).append("<< "); if (!early) { s->append("/EarlyChange 0 "); } @@ -1739,11 +1759,12 @@ RunLengthStream::~RunLengthStream() delete str; } -void RunLengthStream::reset() +bool RunLengthStream::reset() { - str->reset(); bufPtr = bufEnd = buf; eof = false; + + return str->reset(); } int RunLengthStream::getChars(int nChars, unsigned char *buffer) @@ -1768,17 +1789,17 @@ int RunLengthStream::getChars(int nChars, unsigned char *buffer) return n; } -GooString *RunLengthStream::getPSFilter(int psLevel, const char *indent) +std::optional RunLengthStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("/RunLengthDecode filter\n"); + s->append(indent).append("/RunLengthDecode filter\n"); return s; } @@ -1865,32 +1886,32 @@ CCITTFaxStream::~CCITTFaxStream() gfree(codingLine); } -void CCITTFaxStream::ccittReset(bool unfiltered) +bool CCITTFaxStream::ccittReset(bool unfiltered) { - if (unfiltered) { - str->unfilteredReset(); - } else { - str->reset(); - } - row = 0; nextLine2D = encoding < 0; inputBits = 0; a0i = 0; outputBits = 0; buf = EOF; + + if (unfiltered) { + return str->unfilteredReset(); + } else { + return str->reset(); + } } -void CCITTFaxStream::unfilteredReset() +bool CCITTFaxStream::unfilteredReset() { - ccittReset(true); + return ccittReset(true); } -void CCITTFaxStream::reset() +bool CCITTFaxStream::reset() { int code1; - ccittReset(false); + bool resetSuccess = ccittReset(false); if (codingLine != nullptr && refLine != nullptr) { eof = false; @@ -1912,6 +1933,8 @@ void CCITTFaxStream::reset() nextLine2D = !lookBits(1); eatBits(1); } + + return resetSuccess; } inline void CCITTFaxStream::addPixels(int a1, int blackPixels) @@ -2572,18 +2595,18 @@ short CCITTFaxStream::lookBits(int n) return (inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n)); } -GooString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent) +std::optional CCITTFaxStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; char s1[50]; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("<< "); + s->append(indent).append("<< "); if (encoding != 0) { sprintf(s1, "/K %d ", encoding); s->append(s1); @@ -2682,13 +2705,8 @@ DCTStream::~DCTStream() delete str; } -void DCTStream::dctReset(bool unfiltered) +bool DCTStream::dctReset(bool unfiltered) { - if (unfiltered) - str->unfilteredReset(); - else - str->reset(); - progressive = interleaved = false; width = height = 0; numComps = 0; @@ -2698,22 +2716,26 @@ void DCTStream::dctReset(bool unfiltered) gotJFIFMarker = false; gotAdobeMarker = false; restartInterval = 0; + if (unfiltered) + return str->unfilteredReset(); + else + return str->reset(); } -void DCTStream::unfilteredReset() +bool DCTStream::unfilteredReset() { - dctReset(true); + return dctReset(true); } -void DCTStream::reset() +bool DCTStream::reset() { int i, j; - dctReset(false); + bool resetResult = dctReset(false); if (!readHeader()) { y = height; - return; + return false; } // compute MCU size @@ -2756,7 +2778,7 @@ void DCTStream::reset() if (bufWidth <= 0 || bufHeight <= 0 || bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) { error(errSyntaxError, getPos(), "Invalid image size in DCT stream"); y = height; - return; + return false; } for (i = 0; i < numComps; ++i) { frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); @@ -2797,6 +2819,8 @@ void DCTStream::reset() restartMarker = 0xd0; restart(); } + + return resetResult; } void DCTStream::close() @@ -3732,12 +3756,12 @@ bool DCTStream::readHeader() bool DCTStream::readBaselineSOF() { - int length; int prec; int i; int c; - length = read16(); + // read the length + (void)read16(); prec = str->getChar(); height = read16(); width = read16(); @@ -3772,12 +3796,12 @@ bool DCTStream::readBaselineSOF() bool DCTStream::readProgressiveSOF() { - int length; int prec; int i; int c; - length = read16(); + // read the length + (void)read16(); prec = str->getChar(); height = read16(); width = read16(); @@ -4064,17 +4088,17 @@ int DCTStream::read16() return (c1 << 8) + c2; } -GooString *DCTStream::getPSFilter(int psLevel, const char *indent) +std::optional DCTStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 2) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("<< >> /DCTDecode filter\n"); + s->append(indent).append("<< >> /DCTDecode filter\n"); return s; } @@ -4170,20 +4194,12 @@ FlateStream::~FlateStream() if (distCodeTab.codes != fixedDistCodeTab.codes) { gfree(const_cast(distCodeTab.codes)); } - if (pred) { - delete pred; - } + delete pred; delete str; } -void FlateStream::flateReset(bool unfiltered) +bool FlateStream::flateReset(bool unfiltered) { - if (unfiltered) { - str->unfilteredReset(); - } else { - str->reset(); - } - index = 0; remain = 0; codeBuf = 0; @@ -4191,18 +4207,23 @@ void FlateStream::flateReset(bool unfiltered) compressedBlock = false; endOfBlock = true; eof = true; + if (unfiltered) { + return str->unfilteredReset(); + } else { + return str->reset(); + } } -void FlateStream::unfilteredReset() +bool FlateStream::unfilteredReset() { - flateReset(true); + return flateReset(true); } -void FlateStream::reset() +bool FlateStream::reset() { int cmf, flg; - flateReset(false); + bool internalResetResult = flateReset(false); // read header //~ need to look at window size? @@ -4210,22 +4231,24 @@ void FlateStream::reset() cmf = str->getChar(); flg = str->getChar(); if (cmf == EOF || flg == EOF) { - return; + return false; } if ((cmf & 0x0f) != 0x08) { error(errSyntaxError, getPos(), "Unknown compression method in flate stream"); - return; + return false; } if ((((cmf << 8) + flg) % 31) != 0) { error(errSyntaxError, getPos(), "Bad FCHECK in flate stream"); - return; + return false; } if (flg & 0x20) { error(errSyntaxError, getPos(), "FDICT bit set in flate stream"); - return; + return false; } eof = false; + + return internalResetResult; } int FlateStream::getChar() @@ -4282,17 +4305,17 @@ int FlateStream::getRawChar() return doGetRawChar(); } -GooString *FlateStream::getPSFilter(int psLevel, const char *indent) +std::optional FlateStream::getPSFilter(int psLevel, const char *indent) { - GooString *s; + std::optional s; if (psLevel < 3 || pred) { - return nullptr; + return {}; } if (!(s = str->getPSFilter(psLevel, indent))) { - return nullptr; + return {}; } - s->append(indent)->append("<< >> /FlateDecode filter\n"); + s->append(indent).append("<< >> /FlateDecode filter\n"); return s; } @@ -4671,14 +4694,16 @@ BufStream::~BufStream() delete str; } -void BufStream::reset() +bool BufStream::reset() { int i; - str->reset(); + bool success = str->reset(); for (i = 0; i < bufSize; ++i) { buf[i] = str->getChar(); } + + return success; } int BufStream::getChar() @@ -4725,10 +4750,11 @@ FixedLengthEncoder::~FixedLengthEncoder() } } -void FixedLengthEncoder::reset() +bool FixedLengthEncoder::reset() { - str->reset(); count = 0; + + return str->reset(); } int FixedLengthEncoder::getChar() @@ -4771,12 +4797,13 @@ ASCIIHexEncoder::~ASCIIHexEncoder() } } -void ASCIIHexEncoder::reset() +bool ASCIIHexEncoder::reset() { - str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = false; + + return str->reset(); } bool ASCIIHexEncoder::fillBuf() @@ -4821,12 +4848,13 @@ ASCII85Encoder::~ASCII85Encoder() } } -void ASCII85Encoder::reset() +bool ASCII85Encoder::reset() { - str->reset(); bufPtr = bufEnd = buf; lineLen = 0; eof = false; + + return str->reset(); } bool ASCII85Encoder::fillBuf() @@ -4916,11 +4944,12 @@ RunLengthEncoder::~RunLengthEncoder() } } -void RunLengthEncoder::reset() +bool RunLengthEncoder::reset() { - str->reset(); bufPtr = bufEnd = nextEnd = buf; eof = false; + + return str->reset(); } // @@ -5028,11 +5057,11 @@ LZWEncoder::~LZWEncoder() } } -void LZWEncoder::reset() +bool LZWEncoder::reset() { int i; - str->reset(); + bool success = str->reset(); // initialize code table for (i = 0; i < 256; ++i) { @@ -5050,6 +5079,8 @@ void LZWEncoder::reset() outBuf = 256; outBufLen = 9; needEOD = false; + + return success; } int LZWEncoder::getChar() @@ -5177,11 +5208,12 @@ CMYKGrayEncoder::~CMYKGrayEncoder() } } -void CMYKGrayEncoder::reset() +bool CMYKGrayEncoder::reset() { - str->reset(); bufPtr = bufEnd = buf; eof = false; + + return str->reset(); } bool CMYKGrayEncoder::fillBuf() @@ -5226,11 +5258,12 @@ RGBGrayEncoder::~RGBGrayEncoder() } } -void RGBGrayEncoder::reset() +bool RGBGrayEncoder::reset() { - str->reset(); bufPtr = bufEnd = buf; eof = false; + + return str->reset(); } bool RGBGrayEncoder::fillBuf() @@ -5270,12 +5303,14 @@ SplashBitmapCMYKEncoder::SplashBitmapCMYKEncoder(SplashBitmap *bitmapA) : bitmap curLine = height - 1; } -SplashBitmapCMYKEncoder::~SplashBitmapCMYKEncoder() { } +SplashBitmapCMYKEncoder::~SplashBitmapCMYKEncoder() = default; -void SplashBitmapCMYKEncoder::reset() +bool SplashBitmapCMYKEncoder::reset() { bufPtr = width; curLine = height - 1; + + return true; } int SplashBitmapCMYKEncoder::lookChar() diff --git a/poppler/Stream.h b/poppler/Stream.h index 1d645cd..4357fc4 100644 --- a/poppler/Stream.h +++ b/poppler/Stream.h @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Jeff Muizelaar // Copyright (C) 2008 Julien Rebetez -// Copyright (C) 2008, 2010, 2011, 2016-2022 Albert Astals Cid +// Copyright (C) 2008, 2010, 2011, 2016-2022, 2024 Albert Astals Cid // Copyright (C) 2009 Carlos Garcia Campos // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010 Hib Eris @@ -33,6 +33,9 @@ // Copyright (C) 2021 Hubert Figuiere // Copyright (C) 2021 Christian Persch // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024 Fernando Herrera +// Copyright (C) 2024, 2025 Nelson Benítez León // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -45,6 +48,8 @@ #include #include #include +#include +#include #include "poppler-config.h" #include "poppler_private_export.h" @@ -121,8 +126,8 @@ class POPPLER_PRIVATE_EXPORT Stream // Get kind of stream. virtual StreamKind getKind() const = 0; - // Reset stream to beginning. - virtual void reset() = 0; + // Reset stream to beginning. Returns 'false' if stream was found to be invalid, 'true' otherwise. + [[nodiscard]] virtual bool reset() = 0; // Close down the stream. virtual void close(); @@ -148,7 +153,10 @@ class POPPLER_PRIVATE_EXPORT Stream { unsigned char readBuf[4096]; int readChars; - reset(); + if (!reset()) { + s.clear(); + return; + } while ((readChars = doGetChars(4096, readBuf)) != 0) { s.append((const char *)readBuf, readChars); } @@ -165,13 +173,22 @@ class POPPLER_PRIVATE_EXPORT Stream int length = 0; int charsToRead = initialSize; bool continueReading = true; - reset(); + if (!reset()) { + return {}; + } while (continueReading && (readChars = doGetChars(charsToRead, buf.data() + length)) != 0) { length += readChars; if (readChars == charsToRead) { if (lookChar() != EOF) { - size += sizeIncrement; + if (unlikely(checkedAdd(size, sizeIncrement, &size))) { + error(errInternal, -1, "toUnsignedChars size grew too much"); + return {}; + } charsToRead = sizeIncrement; + if (unlikely(static_cast(size) > buf.max_size())) { + error(errInternal, -1, "toUnsignedChars size grew too much"); + return {}; + } buf.resize(size); } else { continueReading = false; @@ -202,7 +219,7 @@ class POPPLER_PRIVATE_EXPORT Stream // Resets the stream without reading anything (even not the headers) // WARNING: Reading the stream with something else than getUnfilteredChar // may lead to unexcepted behaviour until you call reset () - virtual void unfilteredReset() = 0; + [[nodiscard]] virtual bool unfilteredReset() = 0; // Get next line from stream. virtual char *getLine(char *buf, int size); @@ -221,7 +238,7 @@ class POPPLER_PRIVATE_EXPORT Stream virtual void setPos(Goffset pos, int dir = 0) = 0; // Get PostScript command for the filter(s). - virtual GooString *getPSFilter(int psLevel, const char *indent); + virtual std::optional getPSFilter(int psLevel, const char *indent); // Does this stream type potentially contain non-printable chars? virtual bool isBinary(bool last = true) const = 0; @@ -241,7 +258,7 @@ class POPPLER_PRIVATE_EXPORT Stream virtual bool isEncoder() const { return false; } // Get image parameters which are defined by the stream contents. - virtual void getImageParams(int * /*bitsPerComponent*/, StreamColorSpaceMode * /*csMode*/) { } + virtual void getImageParams(int * /*bitsPerComponent*/, StreamColorSpaceMode * /*csMode*/, bool * /*hasAlpha*/) { } // Return the next stream in the "stack". virtual Stream *getNextStream() const { return nullptr; } @@ -294,6 +311,8 @@ class POPPLER_PRIVATE_EXPORT OutStream // Put a char in the stream virtual void put(char c) = 0; + virtual size_t write(std::span data) = 0; + virtual void printf(const char *format, ...) GCC_PRINTF_FORMAT(2, 3) = 0; }; @@ -313,6 +332,8 @@ class POPPLER_PRIVATE_EXPORT FileOutStream : public OutStream void put(char c) override; + size_t write(std::span data) override; + void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: @@ -329,7 +350,6 @@ class POPPLER_PRIVATE_EXPORT FileOutStream : public OutStream class POPPLER_PRIVATE_EXPORT BaseStream : public Stream { public: - // TODO Mirar si puedo hacer que dictA sea un puntero BaseStream(Object &&dictA, Goffset lengthA); ~BaseStream() override; virtual BaseStream *copy() = 0; @@ -371,7 +391,7 @@ class POPPLER_PRIVATE_EXPORT BaseSeekInputStream : public BaseStream BaseSeekInputStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA); ~BaseSeekInputStream() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } @@ -381,7 +401,7 @@ class POPPLER_PRIVATE_EXPORT BaseSeekInputStream : public BaseStream void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } + [[nodiscard]] bool unfilteredReset() override { return reset(); } protected: Goffset start; @@ -427,7 +447,7 @@ class FilterStream : public Stream Stream *getNextStream() const override { return str; } int getUnfilteredChar() override { return str->getUnfilteredChar(); } - void unfilteredReset() override { str->unfilteredReset(); } + [[nodiscard]] bool unfilteredReset() override { return str->unfilteredReset(); } protected: Stream *str; @@ -451,7 +471,7 @@ class POPPLER_PRIVATE_EXPORT ImageStream ImageStream &operator=(const ImageStream &other) = delete; // Reset the stream. - void reset(); + [[nodiscard]] bool reset(); // Close the stream previously reset void close(); @@ -531,7 +551,7 @@ class POPPLER_PRIVATE_EXPORT FileStream : public BaseStream BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return strFile; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } @@ -541,7 +561,7 @@ class POPPLER_PRIVATE_EXPORT FileStream : public BaseStream void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } + [[nodiscard]] bool unfilteredReset() override { return reset(); } bool getNeedsEncryptionOnSave() const { return needsEncryptionOnSave; } void setNeedsEncryptionOnSave(bool needsEncryptionOnSaveA) { needsEncryptionOnSave = needsEncryptionOnSaveA; } @@ -572,7 +592,6 @@ class POPPLER_PRIVATE_EXPORT FileStream : public BaseStream return n; } -private: GooFile *file; Goffset offset; Goffset start; @@ -601,7 +620,7 @@ class POPPLER_PRIVATE_EXPORT CachedFileStream : public BaseStream BaseStream *copy() override; Stream *makeSubStream(Goffset startA, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return strCachedFile; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } @@ -611,7 +630,7 @@ class POPPLER_PRIVATE_EXPORT CachedFileStream : public BaseStream void moveStart(Goffset delta) override; int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } + [[nodiscard]] bool unfilteredReset() override { return reset(); } private: bool fillBuf(); @@ -660,7 +679,11 @@ class BaseMemStream : public BaseStream StreamKind getKind() const override { return strWeird; } - void reset() override { bufPtr = buf + start; } + [[nodiscard]] bool reset() override + { + bufPtr = buf + start; + return true; + } void close() override { } @@ -668,7 +691,7 @@ class BaseMemStream : public BaseStream int lookChar() override { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } - Goffset getPos() override { return (int)(bufPtr - buf); } + Goffset getPos() override { return bufPtr - buf; } void setPos(Goffset pos, int dir = 0) override { @@ -698,7 +721,7 @@ class BaseMemStream : public BaseStream int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } + bool unfilteredReset() override { return reset(); } protected: T *buf; @@ -771,7 +794,7 @@ class POPPLER_PRIVATE_EXPORT EmbedStream : public BaseStream BaseStream *copy() override; Stream *makeSubStream(Goffset start, bool limitedA, Goffset lengthA, Object &&dictA) override; StreamKind getKind() const override { return str->getKind(); } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; Goffset getPos() override; @@ -780,7 +803,7 @@ class POPPLER_PRIVATE_EXPORT EmbedStream : public BaseStream void moveStart(Goffset delta) override; int getUnfilteredChar() override { return str->getUnfilteredChar(); } - void unfilteredReset() override { str->unfilteredReset(); } + [[nodiscard]] bool unfilteredReset() override { return str->unfilteredReset(); } void rewind(); void restore(); @@ -811,7 +834,7 @@ class ASCIIHexStream : public FilterStream explicit ASCIIHexStream(Stream *strA); ~ASCIIHexStream() override; StreamKind getKind() const override { return strASCIIHex; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { int c = lookChar(); @@ -819,7 +842,7 @@ class ASCIIHexStream : public FilterStream return c; } int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: @@ -837,7 +860,7 @@ class ASCII85Stream : public FilterStream explicit ASCII85Stream(Stream *strA); ~ASCII85Stream() override; StreamKind getKind() const override { return strASCII85; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { int ch = lookChar(); @@ -845,7 +868,7 @@ class ASCII85Stream : public FilterStream return ch; } int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: @@ -865,12 +888,12 @@ class LZWStream : public FilterStream LZWStream(Stream *strA, int predictor, int columns, int colors, int bits, int earlyA); ~LZWStream() override; StreamKind getKind() const override { return strLZW; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; int getRawChar() override; void getRawChars(int nChars, int *buffer) override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: @@ -925,10 +948,10 @@ class RunLengthStream : public FilterStream explicit RunLengthStream(Stream *strA); ~RunLengthStream() override; StreamKind getKind() const override { return strRunLength; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; private: @@ -955,7 +978,7 @@ class CCITTFaxStream : public FilterStream CCITTFaxStream(Stream *strA, int encodingA, bool endOfLineA, bool byteAlignA, int columnsA, int rowsA, bool endOfBlockA, bool blackA, int damagedRowsBeforeErrorA); ~CCITTFaxStream() override; StreamKind getKind() const override { return strCCITTFax; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { int c = lookChar(); @@ -963,10 +986,10 @@ class CCITTFaxStream : public FilterStream return c; } int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; - void unfilteredReset() override; + [[nodiscard]] bool unfilteredReset() override; int getEncoding() { return encoding; } bool getEndOfLine() { return endOfLine; } @@ -977,7 +1000,7 @@ class CCITTFaxStream : public FilterStream int getDamagedRowsBeforeError() { return damagedRowsBeforeError; } private: - void ccittReset(bool unfiltered); + [[nodiscard]] bool ccittReset(bool unfiltered); int encoding; // 'K' parameter bool endOfLine; // 'EndOfLine' parameter bool byteAlign; // 'EncodedByteAlign' parameter @@ -1052,17 +1075,17 @@ class DCTStream : public FilterStream DCTStream(Stream *strA, int colorXformA, Dict *dict, int recursion); ~DCTStream() override; StreamKind getKind() const override { return strDCT; } - void reset() override; + [[nodiscard]] bool reset() override; void close() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; - void unfilteredReset() override; + [[nodiscard]] bool unfilteredReset() override; private: - void dctReset(bool unfiltered); + [[nodiscard]] bool dctReset(bool unfiltered); bool progressive; // set if in progressive mode bool interleaved; // set if in interleaved mode int width, height; // image size @@ -1156,17 +1179,17 @@ class FlateStream : public FilterStream FlateStream(Stream *strA, int predictor, int columns, int colors, int bits); ~FlateStream() override; StreamKind getKind() const override { return strFlate; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; int getRawChar() override; void getRawChars(int nChars, int *buffer) override; - GooString *getPSFilter(int psLevel, const char *indent) override; + std::optional getPSFilter(int psLevel, const char *indent) override; bool isBinary(bool last = true) const override; - void unfilteredReset() override; + [[nodiscard]] bool unfilteredReset() override; private: - void flateReset(bool unfiltered); + [[nodiscard]] bool flateReset(bool unfiltered); inline int doGetRawChar() { int c; @@ -1232,10 +1255,10 @@ class EOFStream : public FilterStream explicit EOFStream(Stream *strA); ~EOFStream() override; StreamKind getKind() const override { return strWeird; } - void reset() override { } + [[nodiscard]] bool reset() override { return true; } int getChar() override { return EOF; } int lookChar() override { return EOF; } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return false; } }; @@ -1249,10 +1272,10 @@ class BufStream : public FilterStream BufStream(Stream *strA, int bufSizeA); ~BufStream() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } + std::optional getPSFilter(int psLevel, const char *indent) override { return {}; } bool isBinary(bool last = true) const override; int lookChar(int idx); @@ -1272,10 +1295,10 @@ class FixedLengthEncoder : public FilterStream FixedLengthEncoder(Stream *strA, int lengthA); ~FixedLengthEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override; bool isEncoder() const override { return true; } @@ -1294,10 +1317,10 @@ class ASCIIHexEncoder : public FilterStream explicit ASCIIHexEncoder(Stream *strA); ~ASCIIHexEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } @@ -1321,10 +1344,10 @@ class ASCII85Encoder : public FilterStream explicit ASCII85Encoder(Stream *strA); ~ASCII85Encoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } @@ -1348,10 +1371,10 @@ class RunLengthEncoder : public FilterStream explicit RunLengthEncoder(Stream *strA); ~RunLengthEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return true; } bool isEncoder() const override { return true; } @@ -1382,10 +1405,10 @@ class LZWEncoder : public FilterStream explicit LZWEncoder(Stream *strA); ~LZWEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int psLevel, const char *indent) override { return nullptr; } + std::optional getPSFilter(int psLevel, const char *indent) override { return {}; } bool isBinary(bool last = true) const override { return true; } bool isEncoder() const override { return true; } @@ -1412,10 +1435,10 @@ class CMYKGrayEncoder : public FilterStream explicit CMYKGrayEncoder(Stream *strA); ~CMYKGrayEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } @@ -1438,10 +1461,10 @@ class RGBGrayEncoder : public FilterStream explicit RGBGrayEncoder(Stream *strA); ~RGBGrayEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } int lookChar() override { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return false; } bool isEncoder() const override { return true; } @@ -1467,10 +1490,10 @@ class SplashBitmapCMYKEncoder : public Stream explicit SplashBitmapCMYKEncoder(SplashBitmap *bitmapA); ~SplashBitmapCMYKEncoder() override; StreamKind getKind() const override { return strWeird; } - void reset() override; + [[nodiscard]] bool reset() override; int getChar() override; int lookChar() override; - GooString *getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return nullptr; } + std::optional getPSFilter(int /*psLevel*/, const char * /*indent*/) override { return {}; } bool isBinary(bool /*last = true*/) const override { return true; } // Although we are an encoder, we return false here, since we do not want do be auto-deleted by @@ -1478,7 +1501,7 @@ class SplashBitmapCMYKEncoder : public Stream bool isEncoder() const override { return false; } int getUnfilteredChar() override { return getChar(); } - void unfilteredReset() override { reset(); } + [[nodiscard]] bool unfilteredReset() override { return reset(); } BaseStream *getBaseStream() override { return nullptr; } Stream *getUndecodedStream() override { return this; } @@ -1501,4 +1524,38 @@ class SplashBitmapCMYKEncoder : public Stream bool fillBuf(); }; +//------------------------------------------------------------------------ +// Object Stream accessors. +//------------------------------------------------------------------------ + +inline bool Object::streamReset() +{ + OBJECT_TYPE_CHECK(objStream); + return stream->reset(); +} + +inline void Object::streamClose() +{ + OBJECT_TYPE_CHECK(objStream); + stream->close(); +} + +inline int Object::streamGetChar() +{ + OBJECT_TYPE_CHECK(objStream); + return stream->getChar(); +} + +inline int Object::streamGetChars(int nChars, unsigned char *buffer) +{ + OBJECT_TYPE_CHECK(objStream); + return stream->doGetChars(nChars, buffer); +} + +inline Dict *Object::streamGetDict() const +{ + OBJECT_TYPE_CHECK(objStream); + return stream->getDict(); +} + #endif diff --git a/poppler/StructElement.cc b/poppler/StructElement.cc index 0808ce9..af27fcd 100644 --- a/poppler/StructElement.cc +++ b/poppler/StructElement.cc @@ -6,11 +6,12 @@ // // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Luigi Scarso -// Copyright 2014, 2017-2019, 2021 Albert Astals Cid +// Copyright 2014, 2017-2019, 2021, 2023, 2024 Albert Astals Cid // Copyright 2015 Dmytro Morgun -// Copyright 2018, 2021 Adrian Johnson +// Copyright 2018, 2021, 2023 Adrian Johnson // Copyright 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright 2018 Adam Reichold +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -154,15 +155,15 @@ static bool isTextString(Object *value) static bool name(Object *value) \ { \ if (!value->isArray()) \ - return allowSingle ? checkItem(value) : false; \ + return (allowSingle) ? checkItem(value) : false; \ \ - if (length && value->arrayGetLength() != length) \ + if ((length) && value->arrayGetLength() != (length)) \ return false; \ \ bool okay = true; \ for (int i = 0; i < value->arrayGetLength(); i++) { \ Object obj = value->arrayGet(i); \ - if ((!allowNulls && obj.isNull()) || !checkItem(&obj)) { \ + if ((!(allowNulls) && obj.isNull()) || !checkItem(&obj)) { \ okay = false; \ break; \ } \ @@ -193,7 +194,7 @@ struct AttributeMapEntry struct AttributeDefaults { - AttributeDefaults() {}; // needed to support old clang + AttributeDefaults() = default; // needed to support old clang Object Inline = Object(objName, "Inline"); Object LrTb = Object(objName, "LrTb"); @@ -210,20 +211,11 @@ struct AttributeDefaults static const AttributeDefaults attributeDefaults; -#define ATTR_LIST_END \ - { \ - Attribute::Unknown, nullptr, nullptr, false, nullptr \ - } +#define ATTR_LIST_END { Attribute::Unknown, nullptr, nullptr, false, nullptr } -#define ATTR_WITH_DEFAULT(name, inherit, check, defval) \ - { \ - Attribute::name, #name, &attributeDefaults.defval, inherit, check \ - } +#define ATTR_WITH_DEFAULT(name, inherit, check, defval) { Attribute::name, #name, &attributeDefaults.defval, inherit, check } -#define ATTR(name, inherit, check) \ - { \ - Attribute::name, #name, nullptr, inherit, check \ - } +#define ATTR(name, inherit, check) { Attribute::name, #name, nullptr, inherit, check } static const AttributeMapEntry attributeMapCommonShared[] = { ATTR_WITH_DEFAULT(Placement, false, isPlacementName, Inline), ATTR_WITH_DEFAULT(WritingMode, true, isWritingModeName, LrTb), @@ -547,7 +539,7 @@ static StructElement::Type nameToType(const char *name) // Attribute //------------------------------------------------------------------------ -Attribute::Attribute(GooString &&nameA, Object *valueA) : type(UserProperty), owner(UserProperties), revision(0), name(std::move(nameA)), value(), hidden(false), formatted(nullptr) +Attribute::Attribute(GooString &&nameA, Object *valueA) : type(UserProperty), owner(UserProperties), revision(0), name(std::move(nameA)), hidden(false) { assert(valueA); value = valueA->copy(); @@ -557,10 +549,7 @@ Attribute::Attribute(Type typeA, Object *valueA) : type(typeA), owner(UserProperties), // TODO: Determine corresponding owner from Type revision(0), - name(), - value(), - hidden(false), - formatted(nullptr) + hidden(false) { assert(valueA); @@ -571,10 +560,7 @@ Attribute::Attribute(Type typeA, Object *valueA) } } -Attribute::~Attribute() -{ - delete formatted; -} +Attribute::~Attribute() = default; const char *Attribute::getTypeName() const { @@ -607,11 +593,10 @@ void Attribute::setFormattedValue(const char *formattedA) if (formatted) { formatted->Set(formattedA); } else { - formatted = new GooString(formattedA); + formatted = GooString(formattedA); } } else { - delete formatted; - formatted = nullptr; + formatted = {}; } } @@ -699,11 +684,6 @@ StructElement::StructData::StructData() : altText(nullptr), actualText(nullptr), StructElement::StructData::~StructData() { - delete altText; - delete actualText; - delete id; - delete title; - delete language; for (StructElement *element : elements) { delete element; } @@ -712,7 +692,7 @@ StructElement::StructData::~StructData() } } -StructElement::StructElement(Dict *element, StructTreeRoot *treeRootA, StructElement *parentA, std::set &seen) : type(Unknown), treeRoot(treeRootA), parent(parentA), s(new StructData()) +StructElement::StructElement(Dict *element, StructTreeRoot *treeRootA, StructElement *parentA, RefRecursionChecker &seen) : type(Unknown), treeRoot(treeRootA), parent(parentA), s(new StructData()) { assert(treeRoot); assert(element); @@ -779,6 +759,15 @@ bool StructElement::getPageRef(Ref &ref) const return false; } +bool StructElement::getStmRef(Ref &ref) const +{ + if (stmRef.isRef()) { + ref = stmRef.getRef(); + return true; + } + return false; +} + const char *StructElement::getTypeName() const { return typeToName(type); @@ -1055,7 +1044,7 @@ void StructElement::parse(Dict *element) } } -StructElement *StructElement::parseChild(const Object *ref, Object *childObj, std::set &seen) +StructElement *StructElement::parseChild(const Object *ref, Object *childObj, RefRecursionChecker &seen) { assert(childObj); assert(ref); @@ -1107,8 +1096,7 @@ StructElement *StructElement::parseChild(const Object *ref, Object *childObj, st } else if (childObj->isDict()) { if (!ref->isRef()) { error(errSyntaxError, -1, "Structure element dictionary is not an indirect reference ({0:s})", ref->getTypeName()); - } else if (seen.find(ref->getRefNum()) == seen.end()) { - seen.insert(ref->getRefNum()); + } else if (seen.insert(ref->getRef())) { child = new StructElement(childObj->getDict(), treeRoot, this, seen); } else { error(errSyntaxWarning, -1, "Loop detected in structure tree, skipping subtree at object {0:d}:{1:d}", ref->getRefNum(), ref->getRefGen()); @@ -1132,7 +1120,7 @@ StructElement *StructElement::parseChild(const Object *ref, Object *childObj, st return child; } -void StructElement::parseChildren(Dict *element, std::set &seen) +void StructElement::parseChildren(Dict *element, RefRecursionChecker &seen) { Object kids = element->lookup("K"); if (kids.isArray()) { diff --git a/poppler/StructElement.h b/poppler/StructElement.h index 7369055..57feac3 100644 --- a/poppler/StructElement.h +++ b/poppler/StructElement.h @@ -6,9 +6,10 @@ // // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Luigi Scarso -// Copyright 2014, 2018, 2019, 2021 Albert Astals Cid +// Copyright 2014, 2018, 2019, 2021, 2023, 2024 Albert Astals Cid // Copyright 2018 Adam Reichold -// Copyright 2021 Adrian Johnson +// Copyright 2021, 2023 Adrian Johnson +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -128,8 +129,7 @@ class POPPLER_PRIVATE_EXPORT Attribute const Object *getValue() const { return &value; } static Object *getDefaultValue(Type type); - // The caller gets the ownership of the return GooString and is responsible of deleting it - std::unique_ptr getName() const { return std::make_unique(type == UserProperty ? name.c_str() : getTypeName()); } + const char *getName() const { return (type == UserProperty ? name.c_str() : getTypeName()); } // The revision is optional, and defaults to zero. unsigned int getRevision() const { return revision; } @@ -153,7 +153,7 @@ class POPPLER_PRIVATE_EXPORT Attribute GooString name; Object value; bool hidden; - GooString *formatted; + std::optional formatted; bool checkType(StructElement *element = nullptr); static Type getTypeForName(const char *name, StructElement *element = nullptr); @@ -242,27 +242,30 @@ class POPPLER_PRIVATE_EXPORT StructElement int getMCID() const { return c->mcid; } Ref getObjectRef() const { return c->ref; } - Ref getParentRef() { return isContent() ? parent->getParentRef() : s->parentRef; } + Ref getParentRef() const { return isContent() ? parent->getParentRef() : s->parentRef; } + StructElement *getParent() const { return parent; } // returns NULL if parent is StructTreeRoot bool hasPageRef() const; bool getPageRef(Ref &ref) const; + bool hasStmRef() const { return stmRef.isRef(); } + bool getStmRef(Ref &ref) const; StructTreeRoot *getStructTreeRoot() { return treeRoot; } // Optional element identifier. - const GooString *getID() const { return isContent() ? nullptr : s->id; } - GooString *getID() { return isContent() ? nullptr : s->id; } + const GooString *getID() const { return isContent() ? nullptr : s->id.get(); } + GooString *getID() { return isContent() ? nullptr : s->id.get(); } // Optional ISO language name, e.g. en_US GooString *getLanguage() { if (!isContent() && s->language) { - return s->language; + return s->language.get(); } return parent ? parent->getLanguage() : nullptr; } const GooString *getLanguage() const { if (!isContent() && s->language) { - return s->language; + return s->language.get(); } return parent ? parent->getLanguage() : nullptr; } @@ -277,12 +280,12 @@ class POPPLER_PRIVATE_EXPORT StructElement } // Optional element title, in human-readable form. - const GooString *getTitle() const { return isContent() ? nullptr : s->title; } - GooString *getTitle() { return isContent() ? nullptr : s->title; } + const GooString *getTitle() const { return isContent() ? nullptr : s->title.get(); } + GooString *getTitle() { return isContent() ? nullptr : s->title.get(); } // Optional element expanded abbreviation text. - const GooString *getExpandedAbbr() const { return isContent() ? nullptr : s->expandedAbbr; } - GooString *getExpandedAbbr() { return isContent() ? nullptr : s->expandedAbbr; } + const GooString *getExpandedAbbr() const { return isContent() ? nullptr : s->expandedAbbr.get(); } + GooString *getExpandedAbbr() { return isContent() ? nullptr : s->expandedAbbr.get(); } unsigned getNumChildren() const { return isContent() ? 0 : s->elements.size(); } const StructElement *getChild(int i) const { return isContent() ? nullptr : s->elements.at(i); } @@ -308,11 +311,11 @@ class POPPLER_PRIVATE_EXPORT StructElement const Attribute *findAttribute(Attribute::Type attributeType, bool inherit = false, Attribute::Owner owner = Attribute::UnknownOwner) const; - const GooString *getAltText() const { return isContent() ? nullptr : s->altText; } - GooString *getAltText() { return isContent() ? nullptr : s->altText; } + const GooString *getAltText() const { return isContent() ? nullptr : s->altText.get(); } + GooString *getAltText() { return isContent() ? nullptr : s->altText.get(); } - const GooString *getActualText() const { return isContent() ? nullptr : s->actualText; } - GooString *getActualText() { return isContent() ? nullptr : s->actualText; } + const GooString *getActualText() const { return isContent() ? nullptr : s->actualText.get(); } + GooString *getActualText() { return isContent() ? nullptr : s->actualText.get(); } // Content text referenced by the element: // @@ -329,7 +332,7 @@ class POPPLER_PRIVATE_EXPORT StructElement // GooString *getText(bool recursive = true) const { return appendSubTreeText(nullptr, recursive); } - const TextSpanArray getTextSpans() const + TextSpanArray getTextSpans() const { if (!isContent()) { return TextSpanArray(); @@ -350,12 +353,12 @@ class POPPLER_PRIVATE_EXPORT StructElement struct StructData { Ref parentRef; - GooString *altText; - GooString *actualText; - GooString *id; - GooString *title; - GooString *expandedAbbr; - GooString *language; + std::unique_ptr altText; + std::unique_ptr actualText; + std::unique_ptr id; + std::unique_ptr title; + std::unique_ptr expandedAbbr; + std::unique_ptr language; unsigned int revision; ElemPtrArray elements; AttrPtrArray attributes; @@ -391,13 +394,13 @@ class POPPLER_PRIVATE_EXPORT StructElement ContentData *c; }; - StructElement(Dict *elementDict, StructTreeRoot *treeRootA, StructElement *parentA, std::set &seen); + StructElement(Dict *elementDict, StructTreeRoot *treeRootA, StructElement *parentA, RefRecursionChecker &seen); StructElement(int mcid, StructTreeRoot *treeRootA, StructElement *parentA); StructElement(const Ref ref, StructTreeRoot *treeRootA, StructElement *parentA); void parse(Dict *elementDict); - StructElement *parseChild(const Object *ref, Object *childObj, std::set &seen); - void parseChildren(Dict *element, std::set &seen); + StructElement *parseChild(const Object *ref, Object *childObj, RefRecursionChecker &seen); + void parseChildren(Dict *element, RefRecursionChecker &seen); void parseAttributes(Dict *attributes, bool keepExisting = false); friend class StructTreeRoot; diff --git a/poppler/StructTreeRoot.cc b/poppler/StructTreeRoot.cc index 47adac9..99ddbfe 100644 --- a/poppler/StructTreeRoot.cc +++ b/poppler/StructTreeRoot.cc @@ -7,7 +7,7 @@ // Copyright 2013, 2014 Igalia S.L. // Copyright 2014 Fabio D'Urso // Copyright 2017 Jan-Erik S -// Copyright 2017-2019 Albert Astals Cid +// Copyright 2017-2019, 2023 Albert Astals Cid // Copyright 2017, 2018 Adrian Johnson // Copyright 2018, Adam Reichold // @@ -54,7 +54,7 @@ void StructTreeRoot::parse(Dict *root) parseNumberTreeNode(parentTreeObj.getDict()); } - std::set seenElements; + RefRecursionChecker seenElements; // Parse the children StructElements const bool marked = doc->getCatalog()->getMarkInfo() & Catalog::markInfoMarked; @@ -66,7 +66,7 @@ void StructTreeRoot::parse(Dict *root) for (int i = 0; i < kids.arrayGetLength(); i++) { const Object &ref = kids.arrayGetNF(i); if (ref.isRef()) { - seenElements.insert(ref.getRefNum()); + seenElements.insert(ref.getRef()); } Object obj = kids.arrayGet(i); if (obj.isDict()) { diff --git a/poppler/StructTreeRoot.h b/poppler/StructTreeRoot.h index 8812a50..7098761 100644 --- a/poppler/StructTreeRoot.h +++ b/poppler/StructTreeRoot.h @@ -5,7 +5,7 @@ // This file is licensed under the GPLv2 or later // // Copyright 2013, 2014 Igalia S.L. -// Copyright 2018, 2019 Albert Astals Cid +// Copyright 2018, 2019, 2024 Albert Astals Cid // Copyright 2018 Adrian Johnson // Copyright 2018 Adam Reichold // @@ -22,7 +22,7 @@ class Dict; class PDFDoc; -class StructTreeRoot +class POPPLER_PRIVATE_EXPORT StructTreeRoot { public: StructTreeRoot(PDFDoc *docA, Dict *rootDict); @@ -63,13 +63,8 @@ class StructTreeRoot // object references and pointers to StructElement objects. struct Parent { - Ref ref; - StructElement *element; - - Parent() : element(nullptr) { ref = Ref::INVALID(); } - Parent(const Parent &p) = default; - Parent &operator=(const Parent &) = default; - ~Parent() { } + Ref ref = Ref::INVALID(); + StructElement *element = nullptr; }; PDFDoc *doc; diff --git a/poppler/TextOutputDev.cc b/poppler/TextOutputDev.cc index 06fc307..9f5cec8 100644 --- a/poppler/TextOutputDev.cc +++ b/poppler/TextOutputDev.cc @@ -20,7 +20,7 @@ // Copyright (C) 2006 Jeff Muizelaar // Copyright (C) 2007, 2008, 2012, 2017 Adrian Johnson // Copyright (C) 2008 Koji Otani -// Copyright (C) 2008, 2010-2012, 2014-2022 Albert Astals Cid +// Copyright (C) 2008, 2010-2012, 2014-2022, 2024, 2025 Albert Astals Cid // Copyright (C) 2008 Pino Toscano // Copyright (C) 2008, 2010 Hib Eris // Copyright (C) 2009 Ross Moore @@ -39,11 +39,14 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Sanchit Anand // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2018-2022 Nelson Benítez León +// Copyright (C) 2018-2022, 2024 Nelson Benítez León // Copyright (C) 2019 Christian Persch // Copyright (C) 2019, 2022 Oliver Sander // Copyright (C) 2019 Dan Shea // Copyright (C) 2021 Peter Williams +// Copyright (C) 2024 Adam Sampson +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2024, 2025 Stefan Brüns // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -59,6 +62,7 @@ #include #include #include +#include #if defined(_WIN32) || defined(__CYGWIN__) # include // for O_BINARY # include // for _setmode @@ -135,7 +139,7 @@ double TextOutputDev::minColSpacing1_default = 0.7; // Minimum spacing between characters within a word, as a fraction of // the font size. -#define minCharSpacing -0.5 +#define minCharSpacing (-0.5) // Maximum spacing between characters within a word, as a fraction of // the font size, when there is no obvious extra-wide character @@ -161,7 +165,7 @@ double TextOutputDev::minColSpacing1_default = 0.7; // Min distance between baseline and underline (in points). //~ this should be font-size-dependent -#define minUnderlineGap -2 +#define minUnderlineGap (-2) // Max distance between baseline and underline (in points). //~ this should be font-size-dependent @@ -330,7 +334,7 @@ class TextUnderline y1 = y1A; horiz = y0 == y1; } - ~TextUnderline() { } + ~TextUnderline() = default; double x0, y0, x1, y1; bool horiz; @@ -351,7 +355,7 @@ class TextLink yMax = yMaxA; link = linkA; } - ~TextLink() { } + ~TextLink() = default; int xMin, yMin, xMax, yMax; AnnotLink *link; @@ -365,7 +369,16 @@ TextFontInfo::TextFontInfo(const GfxState *state) { gfxFont = state->getFont(); #ifdef TEXTOUT_WORD_LIST - fontName = (gfxFont && gfxFont->getName()) ? new GooString(*gfxFont->getName()) : nullptr; + if (gfxFont) { + const std::optional &gfxFontName = gfxFont->getName(); + if (gfxFontName) { + fontName = new GooString(*gfxFontName); + } else { + fontName = nullptr; + } + } else { + fontName = nullptr; + } flags = gfxFont ? gfxFont->getFlags() : 0; #endif } @@ -373,9 +386,7 @@ TextFontInfo::TextFontInfo(const GfxState *state) TextFontInfo::~TextFontInfo() { #ifdef TEXTOUT_WORD_LIST - if (fontName) { - delete fontName; - } + delete fontName; #endif } @@ -391,7 +402,7 @@ bool TextFontInfo::matches(const TextFontInfo *fontInfo) const bool TextFontInfo::matches(const Ref *ref) const { - return (*(gfxFont->getID()) == *ref); + return gfxFont && (*(gfxFont->getID()) == *ref); } double TextFontInfo::getAscent() const @@ -417,13 +428,6 @@ TextWord::TextWord(const GfxState *state, int rotA, double fontSizeA) { rot = rotA; fontSize = fontSizeA; - text = nullptr; - charcode = nullptr; - edge = nullptr; - charPos = nullptr; - font = nullptr; - textMat = nullptr; - len = size = 0; spaceAfter = false; next = nullptr; invisible = state->getRender() == 3; @@ -445,27 +449,14 @@ TextWord::TextWord(const GfxState *state, int rotA, double fontSizeA) link = nullptr; } -TextWord::~TextWord() -{ - gfree(text); - gfree(charcode); - gfree(edge); - gfree(charPos); - gfree(font); - gfree(textMat); -} +TextWord::~TextWord() = default; void TextWord::addChar(const GfxState *state, TextFontInfo *fontA, double x, double y, double dx, double dy, int charPosA, int charLen, CharCode c, Unicode u, const Matrix &textMatA) { - ensureCapacity(len + 1); - text[len] = u; - charcode[len] = c; - charPos[len] = charPosA; - charPos[len + 1] = charPosA + charLen; - font[len] = fontA; - textMat[len] = textMatA; + chars.push_back(CharInfo { u, c, charPosA, 0.0, fontA, textMatA }); + charPosEnd = charPosA + charLen; - if (len == 0) { + if (len() == 1) { setInitialBounds(fontA, x, y); } @@ -474,43 +465,42 @@ void TextWord::addChar(const GfxState *state, TextFontInfo *fontA, double x, dou // TextPage::beginWord()) for vertical writing mode switch (rot) { case 0: - edge[len] = x - fontSize; - xMax = edge[len + 1] = x; + chars.back().edge = x - fontSize; + xMax = edgeEnd = x; break; case 1: - edge[len] = y - fontSize; - yMax = edge[len + 1] = y; + chars.back().edge = y - fontSize; + yMax = edgeEnd = y; break; case 2: - edge[len] = x + fontSize; - xMin = edge[len + 1] = x; + chars.back().edge = x + fontSize; + xMin = edgeEnd = x; break; case 3: - edge[len] = y + fontSize; - yMin = edge[len + 1] = y; + chars.back().edge = y + fontSize; + yMin = edgeEnd = y; break; } } else { // horizontal writing mode switch (rot) { case 0: - edge[len] = x; - xMax = edge[len + 1] = x + dx; + chars.back().edge = x; + xMax = edgeEnd = x + dx; break; case 1: - edge[len] = y; - yMax = edge[len + 1] = y + dy; + chars.back().edge = y; + yMax = edgeEnd = y + dy; break; case 2: - edge[len] = x; - xMin = edge[len + 1] = x + dx; + chars.back().edge = x; + xMin = edgeEnd = x + dx; break; case 3: - edge[len] = y; - yMin = edge[len + 1] = y + dy; + chars.back().edge = y; + yMin = edgeEnd = y + dy; break; } } - ++len; } void TextWord::setInitialBounds(TextFontInfo *fontA, double x, double y) @@ -602,19 +592,6 @@ void TextWord::setInitialBounds(TextFontInfo *fontA, double x, double y) } } -void TextWord::ensureCapacity(int capacity) -{ - if (capacity > size) { - size = std::max(size + 16, capacity); - text = (Unicode *)greallocn(text, size, sizeof(Unicode)); - charcode = (CharCode *)greallocn(charcode, (size + 1), sizeof(CharCode)); - edge = (double *)greallocn(edge, (size + 1), sizeof(double)); - charPos = (int *)greallocn(charPos, size + 1, sizeof(int)); - font = (TextFontInfo **)greallocn(font, size, sizeof(TextFontInfo *)); - textMat = (Matrix *)greallocn(textMat, size, sizeof(Matrix)); - } -} - struct CombiningTable { Unicode base; @@ -649,19 +626,15 @@ static Unicode getCombiningChar(Unicode u) bool TextWord::addCombining(const GfxState *state, TextFontInfo *fontA, double fontSizeA, double x, double y, double dx, double dy, int charPosA, int charLen, CharCode c, Unicode u, const Matrix &textMatA) { - if (len == 0 || wMode != 0 || fontA->getWMode() != 0) { + if (chars.empty() || wMode != 0 || fontA->getWMode() != 0) { return false; } Unicode cCurrent = getCombiningChar(u); - Unicode cPrev = getCombiningChar(text[len - 1]); - double edgeMid = (edge[len - 1] + edge[len]) / 2; - double charMid, maxScaledMidDelta, charBase, maxScaledBaseDelta; - - if (cCurrent != 0 && unicodeTypeAlphaNum(text[len - 1])) { + if (cCurrent != 0 && unicodeTypeAlphaNum(chars.back().text)) { // Current is a combining character, previous is base character - maxScaledMidDelta = fabs(edge[len] - edge[len - 1]) * combMaxMidDelta; - charMid = charBase = maxScaledBaseDelta = 0; + double maxScaledMidDelta = fabs(edgeEnd - chars.back().edge) * combMaxMidDelta; + double charMid, charBase, maxScaledBaseDelta; // Test if characters overlap if (rot == 0 || rot == 2) { @@ -674,29 +647,24 @@ bool TextWord::addCombining(const GfxState *state, TextFontInfo *fontA, double f maxScaledBaseDelta = (xMax - xMin) * combMaxBaseDelta; } + double edgeMid = (chars.back().edge + edgeEnd) / 2; if (fabs(charMid - edgeMid) >= maxScaledMidDelta || fabs(charBase - base) >= maxScaledBaseDelta) { return false; } // Add character, but don't adjust edge / bounding box because // combining character's positioning could be odd. - ensureCapacity(len + 1); - text[len] = cCurrent; - charcode[len] = c; - charPos[len] = charPosA; - charPos[len + 1] = charPosA + charLen; - font[len] = fontA; - textMat[len] = textMatA; - edge[len + 1] = edge[len]; - edge[len] = (edge[len + 1] + edge[len - 1]) / 2; - ++len; + chars.emplace_back(CharInfo { cCurrent, c, charPosA, edgeMid, fontA, textMatA }); + charPosEnd = charPosA + charLen; + return true; } + Unicode cPrev = getCombiningChar(chars.back().text); if (cPrev != 0 && unicodeTypeAlphaNum(u)) { // Previous is a combining character, current is base character - maxScaledBaseDelta = (fontA->getAscent() - fontA->getDescent()) * fontSizeA * combMaxBaseDelta; - charMid = charBase = maxScaledMidDelta = 0; + double maxScaledBaseDelta = (fontA->getAscent() - fontA->getDescent()) * fontSizeA * combMaxBaseDelta; + double charMid, charBase, maxScaledMidDelta; // Test if characters overlap if (rot == 0 || rot == 2) { @@ -709,73 +677,71 @@ bool TextWord::addCombining(const GfxState *state, TextFontInfo *fontA, double f maxScaledMidDelta = fabs(dy * combMaxMidDelta); } + double edgeMid = (chars.back().edge + edgeEnd) / 2; if (fabs(charMid - edgeMid) >= maxScaledMidDelta || fabs(charBase - base) >= maxScaledBaseDelta) { return false; } - // move combining character to after base character - ensureCapacity(len + 1); fontSize = fontSizeA; - text[len] = cPrev; - charcode[len] = charcode[len - 1]; - charPos[len] = charPosA; - charPos[len + 1] = charPosA + charLen; - font[len] = font[len - 1]; - textMat[len] = textMat[len - 1]; - - text[len - 1] = u; - charcode[len - 1] = c; - font[len - 1] = fontA; - textMat[len - 1] = textMatA; - - if (len == 1) { + // move combining character to after base character + chars.emplace_back(CharInfo { cPrev, chars.back().charcode, charPosA, edgeMid, chars.back().font, chars.back().textMat }); + + auto &lastChar = chars[chars.size() - 2]; + + charPosEnd = charPosA + charLen; + lastChar.text = u; + lastChar.charcode = c; + lastChar.font = fontA; + lastChar.textMat = textMatA; + + if (len() == 2) { setInitialBounds(fontA, x, y); } // Updated edges / bounding box because we changed the base // character. if (wMode) { + // FIXME unreachable, wMode == 0 switch (rot) { case 0: - edge[len - 1] = x - fontSize; - xMax = edge[len + 1] = x; + lastChar.edge = x - fontSize; + xMax = edgeEnd = x; break; case 1: - edge[len - 1] = y - fontSize; - yMax = edge[len + 1] = y; + lastChar.edge = y - fontSize; + yMax = edgeEnd = y; break; case 2: - edge[len - 1] = x + fontSize; - xMin = edge[len + 1] = x; + lastChar.edge = x + fontSize; + xMin = edgeEnd = x; break; case 3: - edge[len - 1] = y + fontSize; - yMin = edge[len + 1] = y; + lastChar.edge = y + fontSize; + yMin = edgeEnd = y; break; } } else { switch (rot) { case 0: - edge[len - 1] = x; - xMax = edge[len + 1] = x + dx; + lastChar.edge = x; + xMax = edgeEnd = x + dx; break; case 1: - edge[len - 1] = y; - yMax = edge[len + 1] = y + dy; + lastChar.edge = y; + yMax = edgeEnd = y + dy; break; case 2: - edge[len - 1] = x; - xMin = edge[len + 1] = x + dx; + lastChar.edge = x; + xMin = edgeEnd = x + dx; break; case 3: - edge[len - 1] = y; - yMin = edge[len + 1] = y + dy; + lastChar.edge = y; + yMin = edgeEnd = y + dy; break; } } - edge[len] = (edge[len + 1] + edge[len - 1]) / 2; - ++len; + chars.back().edge = (edgeEnd + lastChar.edge) / 2; return true; } return false; @@ -783,8 +749,6 @@ bool TextWord::addCombining(const GfxState *state, TextFontInfo *fontA, double f void TextWord::merge(TextWord *word) { - int i; - if (word->xMin < xMin) { xMin = word->xMin; } @@ -797,18 +761,9 @@ void TextWord::merge(TextWord *word) if (word->yMax > yMax) { yMax = word->yMax; } - ensureCapacity(len + word->len); - for (i = 0; i < word->len; ++i) { - text[len + i] = word->text[i]; - charcode[len + i] = word->charcode[i]; - edge[len + i] = word->edge[i]; - charPos[len + i] = word->charPos[i]; - font[len + i] = word->font[i]; - textMat[len + i] = word->textMat[i]; - } - edge[len + word->len] = word->edge[word->len]; - charPos[len + word->len] = word->charPos[word->len]; - len += word->len; + chars.insert(chars.end(), word->chars.begin(), word->chars.end()); + edgeEnd = word->edgeEnd; + charPosEnd = word->charPosEnd; } inline int TextWord::primaryCmp(const TextWord *word) const @@ -855,17 +810,15 @@ double TextWord::primaryDelta(const TextWord *word) const return delta; } -int TextWord::cmpYX(const void *p1, const void *p2) +bool TextWord::cmpYX(const TextWord *const word1, const TextWord *const word2) { - TextWord *word1 = *(TextWord **)p1; - TextWord *word2 = *(TextWord **)p2; double cmp; cmp = word1->yMin - word2->yMin; if (cmp == 0) { cmp = word1->xMin - word2->xMin; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } #ifdef TEXTOUT_WORD_LIST @@ -875,14 +828,13 @@ GooString *TextWord::getText() const GooString *s; const UnicodeMap *uMap; char buf[8]; - int n, i; s = new GooString(); if (!(uMap = globalParams->getTextEncoding())) { return s; } - for (i = 0; i < len; ++i) { - n = uMap->mapUnicode(text[i], buf, sizeof(buf)); + for (size_t i = 0; i < len(); ++i) { + auto n = uMap->mapUnicode(chars[i].text, buf, sizeof(buf)); s->append(buf, n); } return s; @@ -890,33 +842,39 @@ GooString *TextWord::getText() const void TextWord::getCharBBox(int charIdx, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const { - if (charIdx < 0 || charIdx >= len) { + if (charIdx < 0) { + return; + } + size_t uCharIdx = charIdx; + if (uCharIdx >= len()) { return; } + auto startingEdge = chars[uCharIdx].edge; + auto endingEdge = (uCharIdx + 1 == len()) ? edgeEnd : chars[charIdx + 1].edge; switch (rot) { case 0: - *xMinA = edge[charIdx]; - *xMaxA = edge[charIdx + 1]; + *xMinA = startingEdge; + *xMaxA = endingEdge; *yMinA = yMin; *yMaxA = yMax; break; case 1: *xMinA = xMin; *xMaxA = xMax; - *yMinA = edge[charIdx]; - *yMaxA = edge[charIdx + 1]; + *yMinA = startingEdge; + *yMaxA = endingEdge; break; case 2: - *xMinA = edge[charIdx + 1]; - *xMaxA = edge[charIdx]; + *xMinA = endingEdge; + *xMaxA = startingEdge; *yMinA = yMin; *yMaxA = yMax; break; case 3: *xMinA = xMin; *xMaxA = xMax; - *yMinA = edge[charIdx + 1]; - *yMaxA = edge[charIdx]; + *yMinA = endingEdge; + *yMaxA = startingEdge; break; } } @@ -931,23 +889,14 @@ TextPool::TextPool() { minBaseIdx = 0; maxBaseIdx = -1; - pool = nullptr; - cursor = nullptr; - cursorBaseIdx = -1; } TextPool::~TextPool() { - int baseIdx; - TextWord *word, *word2; - - for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) { - for (word = pool[baseIdx - minBaseIdx]; word; word = word2) { - word2 = word->next; - delete word; - } + for (auto &wordList : pool) { + delete wordList.head; } - gfree(pool); + pool.resize(0); } int TextPool::getBaseIdx(double base) const @@ -962,74 +911,119 @@ int TextPool::getBaseIdx(double base) const return (int)baseIdxDouble; } -void TextPool::addWord(TextWord *word) +void TextPool::sort() { - int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx; - TextWord *w0, *w1; + const auto SplitWordList = [](TextWord *list) -> TextWord * { + auto slow = list; + auto fast = list->next; + + // fast is advanced twice as often as slow, so when + // fast reaches the end slow points to the midpoint. + while (fast) { + fast = fast->next; + if (fast) { + slow = slow->next; + fast = fast->next; + } + } + auto back = slow->next; + slow->next = nullptr; + return back; + }; + + const auto SortedMerge = [](TextWord *a, TextWord *b) { + if (!a) { + return b; + } else if (!b) { + return a; + } + + TextWord *head = nullptr; + TextWord *cursor = nullptr; + + if (a->primaryCmp(b) <= 0) { + head = cursor = a; + a = a->next; + } else { + head = cursor = b; + b = b->next; + } + + while (a && b) { + if (a->primaryCmp(b) <= 0) { + cursor->next = a; + cursor = cursor->next; + a = a->next; + } else { + cursor->next = b; + cursor = cursor->next; + b = b->next; + } + } + + if (a) { + cursor->next = a; + } else { + cursor->next = b; + } + + return head; + }; + + const std::function SortWordList = [&](TextWord **list) -> void { + if ((*list == nullptr) || ((*list)->next == nullptr)) { + return; + } + + TextWord *second = SplitWordList(*list); + + SortWordList(list); + SortWordList(&second); + + *list = SortedMerge(*list, second); + }; + for (WordList &list : pool) { + if (!list.head) { + continue; + } + SortWordList(&list.head); + } +} + +void TextPool::addWord(TextWord *word) +{ // expand the array if needed - wordBaseIdx = (int)(word->base / textPoolStep); + int wordBaseIdx = (int)(word->base / textPoolStep); + if (unlikely(wordBaseIdx <= INT_MIN + 128 || wordBaseIdx >= INT_MAX - 128)) { error(errSyntaxWarning, -1, "wordBaseIdx out of range"); delete word; return; } - if (minBaseIdx > maxBaseIdx) { + + if (pool.empty()) { minBaseIdx = wordBaseIdx - 128; maxBaseIdx = wordBaseIdx + 128; - pool = (TextWord **)gmallocn(maxBaseIdx - minBaseIdx + 1, sizeof(TextWord *)); - for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) { - pool[baseIdx - minBaseIdx] = nullptr; - } - } else if (wordBaseIdx < minBaseIdx) { - newMinBaseIdx = wordBaseIdx - 128; - TextWord **newPool = (TextWord **)gmallocn_checkoverflow(maxBaseIdx - newMinBaseIdx + 1, sizeof(TextWord *)); - if (unlikely(!newPool)) { - error(errSyntaxWarning, -1, "newPool would overflow"); - delete word; - return; - } - for (baseIdx = newMinBaseIdx; baseIdx < minBaseIdx; ++baseIdx) { - newPool[baseIdx - newMinBaseIdx] = nullptr; - } - memcpy(&newPool[minBaseIdx - newMinBaseIdx], pool, (maxBaseIdx - minBaseIdx + 1) * sizeof(TextWord *)); - gfree(pool); - pool = newPool; - minBaseIdx = newMinBaseIdx; - } else if (wordBaseIdx > maxBaseIdx) { - newMaxBaseIdx = wordBaseIdx + 128; - TextWord **reallocatedPool = (TextWord **)greallocn(pool, newMaxBaseIdx - minBaseIdx + 1, sizeof(TextWord *), true /*checkoverflow*/, false /*free_pool*/); - if (!reallocatedPool) { - error(errSyntaxWarning, -1, "new pool size would overflow"); - delete word; - return; - } - pool = reallocatedPool; - for (baseIdx = maxBaseIdx + 1; baseIdx <= newMaxBaseIdx; ++baseIdx) { - pool[baseIdx - minBaseIdx] = nullptr; - } - maxBaseIdx = newMaxBaseIdx; + pool.resize(257); } - - // insert the new word - if (cursor && wordBaseIdx == cursorBaseIdx && word->primaryCmp(cursor) >= 0) { - w0 = cursor; - w1 = cursor->next; - } else { - w0 = nullptr; - w1 = pool[wordBaseIdx - minBaseIdx]; + while (wordBaseIdx < minBaseIdx) { + pool.insert(pool.begin(), 128, {}); + minBaseIdx -= 128; } - for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) { - ; + while (wordBaseIdx > maxBaseIdx) { + pool.insert(pool.end(), 128, {}); + maxBaseIdx += 128; } - word->next = w1; - if (w0) { - w0->next = word; + + // append the new word + auto &wordList = pool[wordBaseIdx - minBaseIdx]; + if (wordList.tail) { + wordList.tail->next = word; } else { - pool[wordBaseIdx - minBaseIdx] = word; + wordList.head = word; } - cursor = word; - cursorBaseIdx = wordBaseIdx; + wordList.tail = word; } //------------------------------------------------------------------------ @@ -1173,35 +1167,31 @@ int TextLine::cmpYX(const TextLine *line) const return primaryCmp(line); } -int TextLine::cmpXY(const void *p1, const void *p2) +bool TextLine::cmpXY(const TextLine *const line1, const TextLine *const line2) { - TextLine *line1 = *(TextLine **)p1; - TextLine *line2 = *(TextLine **)p2; int cmp; if ((cmp = line1->primaryCmp(line2))) { - return cmp; + return cmp < 0; } - return line1->secondaryCmp(line2); + return line1->secondaryCmp(line2) < 0; } void TextLine::coalesce(const UnicodeMap *uMap) { - TextWord *word0, *word1; double space, delta, minSpace; bool isUnicode; char buf[8]; - int i, j; if (words->next) { // compute the inter-word space threshold - if (words->len > 1 || words->next->len > 1) { + if (words->len() > 1 || words->next->len() > 1) { minSpace = 0; } else { minSpace = words->primaryDelta(words->next); - for (word0 = words->next, word1 = word0->next; word1 && minSpace > 0; word0 = word1, word1 = word0->next) { - if (word1->len > 1) { + for (auto word0 = words->next, word1 = word0->next; word1 && minSpace > 0; word0 = word1, word1 = word0->next) { + if (word1->len() > 1) { minSpace = 0; } delta = word0->primaryDelta(word1); @@ -1220,15 +1210,17 @@ void TextLine::coalesce(const UnicodeMap *uMap) } // merge words - word0 = words; - word1 = words->next; + auto word0 = words; + auto word1 = words->next; while (word1) { if (word0->primaryDelta(word1) >= space) { word0->spaceAfter = true; word0 = word1; word1 = word1->next; - } else if (word0->font[word0->len - 1] == word1->font[0] && word0->underlined == word1->underlined && fabs(word0->fontSize - word1->fontSize) < maxWordFontSizeDelta * words->fontSize - && word1->charPos[0] == word0->charPos[word0->len]) { + } else if (word0->chars.back().font == word1->chars.front().font // + && word0->underlined == word1->underlined // + && fabs(word0->fontSize - word1->fontSize) < maxWordFontSizeDelta * words->fontSize // + && word1->chars.front().charPos == word0->charPosEnd) { word0->merge(word1); word0->next = word1->next; delete word1; @@ -1243,22 +1235,22 @@ void TextLine::coalesce(const UnicodeMap *uMap) // build the line text isUnicode = uMap ? uMap->isUnicode() : false; len = 0; - for (word1 = words; word1; word1 = word1->next) { - len += word1->len; + for (auto word1 = words; word1; word1 = word1->next) { + len += word1->len(); if (word1->spaceAfter) { ++len; } } text = (Unicode *)gmallocn(len, sizeof(Unicode)); edge = (double *)gmallocn(len + 1, sizeof(double)); - i = 0; - for (word1 = words; word1; word1 = word1->next) { - for (j = 0; j < word1->len; ++j) { - text[i] = word1->text[j]; - edge[i] = word1->edge[j]; + size_t i = 0; + for (auto word1 = words; word1; word1 = word1->next) { + for (size_t j = 0; j < word1->len(); ++j) { + text[i] = word1->chars[j].text; + edge[i] = word1->chars[j].edge; ++i; } - edge[i] = word1->edge[word1->len]; + edge[i] = word1->edgeEnd; if (word1->spaceAfter) { text[i] = (Unicode)0x0020; ++i; @@ -1268,12 +1260,12 @@ void TextLine::coalesce(const UnicodeMap *uMap) // compute convertedLen and set up the col array col = (int *)gmallocn(len + 1, sizeof(int)); convertedLen = 0; - for (i = 0; i < len; ++i) { - col[i] = convertedLen; + for (int ci = 0; ci < len; ++ci) { + col[ci] = convertedLen; if (isUnicode) { ++convertedLen; } else if (uMap) { - convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf)); + convertedLen += uMap->mapUnicode(text[ci], buf, sizeof(buf)); } } col[len] = convertedLen; @@ -1301,11 +1293,11 @@ class TextLineFrag void init(TextLine *lineA, int startA, int lenA); void computeCoords(bool oneRot); - static int cmpYXPrimaryRot(const void *p1, const void *p2); - static int cmpYXLineRot(const void *p1, const void *p2); - static int cmpXYLineRot(const void *p1, const void *p2); - static int cmpXYColumnPrimaryRot(const void *p1, const void *p2); - static int cmpXYColumnLineRot(const void *p1, const void *p2); + static bool cmpYXPrimaryRot(const TextLineFrag &frag1, const TextLineFrag &frag2); + static bool cmpYXLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2); + static bool cmpXYLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2); + static bool cmpXYColumnPrimaryRot(const TextLineFrag &frag1, const TextLineFrag &frag2); + static bool cmpXYColumnLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2); }; void TextLineFrag::init(TextLine *lineA, int startA, int lenA) @@ -1445,160 +1437,150 @@ void TextLineFrag::computeCoords(bool oneRot) } } -int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) +bool TextLineFrag::cmpYXPrimaryRot(const TextLineFrag &frag1, const TextLineFrag &frag2) { - TextLineFrag *frag1 = (TextLineFrag *)p1; - TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy - switch (frag1->line->blk->page->primaryRot) { + switch (frag1.line->blk->page->primaryRot) { case 0: - if (fabs(cmp = frag1->yMin - frag2->yMin) < 0.01) { - cmp = frag1->xMin - frag2->xMin; + if (fabs(cmp = frag1.yMin - frag2.yMin) < 0.01) { + cmp = frag1.xMin - frag2.xMin; } break; case 1: - if (fabs(cmp = frag2->xMax - frag1->xMax) < 0.01) { - cmp = frag1->yMin - frag2->yMin; + if (fabs(cmp = frag2.xMax - frag1.xMax) < 0.01) { + cmp = frag1.yMin - frag2.yMin; } break; case 2: - if (fabs(cmp = frag2->yMin - frag1->yMin) < 0.01) { - cmp = frag2->xMax - frag1->xMax; + if (fabs(cmp = frag2.yMin - frag1.yMin) < 0.01) { + cmp = frag2.xMax - frag1.xMax; } break; case 3: - if (fabs(cmp = frag1->xMax - frag2->xMax) < 0.01) { - cmp = frag2->yMax - frag1->yMax; + if (fabs(cmp = frag1.xMax - frag2.xMax) < 0.01) { + cmp = frag2.yMax - frag1.yMax; } break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } -int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) +bool TextLineFrag::cmpYXLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2) { - TextLineFrag *frag1 = (TextLineFrag *)p1; - TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy - switch (frag1->line->rot) { + switch (frag1.line->rot) { case 0: - if ((cmp = frag1->yMin - frag2->yMin) == 0) { - cmp = frag1->xMin - frag2->xMin; + if ((cmp = frag1.yMin - frag2.yMin) == 0) { + cmp = frag1.xMin - frag2.xMin; } break; case 1: - if ((cmp = frag2->xMax - frag1->xMax) == 0) { - cmp = frag1->yMin - frag2->yMin; + if ((cmp = frag2.xMax - frag1.xMax) == 0) { + cmp = frag1.yMin - frag2.yMin; } break; case 2: - if ((cmp = frag2->yMin - frag1->yMin) == 0) { - cmp = frag2->xMax - frag1->xMax; + if ((cmp = frag2.yMin - frag1.yMin) == 0) { + cmp = frag2.xMax - frag1.xMax; } break; case 3: - if ((cmp = frag1->xMax - frag2->xMax) == 0) { - cmp = frag2->yMax - frag1->yMax; + if ((cmp = frag1.xMax - frag2.xMax) == 0) { + cmp = frag2.yMax - frag1.yMax; } break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } -int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) +bool TextLineFrag::cmpXYLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2) { - TextLineFrag *frag1 = (TextLineFrag *)p1; - TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; cmp = 0; // make gcc happy - switch (frag1->line->rot) { + switch (frag1.line->rot) { case 0: - if ((cmp = frag1->xMin - frag2->xMin) == 0) { - cmp = frag1->yMin - frag2->yMin; + if ((cmp = frag1.xMin - frag2.xMin) == 0) { + cmp = frag1.yMin - frag2.yMin; } break; case 1: - if ((cmp = frag1->yMin - frag2->yMin) == 0) { - cmp = frag2->xMax - frag1->xMax; + if ((cmp = frag1.yMin - frag2.yMin) == 0) { + cmp = frag2.xMax - frag1.xMax; } break; case 2: - if ((cmp = frag2->xMax - frag1->xMax) == 0) { - cmp = frag2->yMin - frag1->yMin; + if ((cmp = frag2.xMax - frag1.xMax) == 0) { + cmp = frag2.yMin - frag1.yMin; } break; case 3: - if ((cmp = frag2->yMax - frag1->yMax) == 0) { - cmp = frag1->xMax - frag2->xMax; + if ((cmp = frag2.yMax - frag1.yMax) == 0) { + cmp = frag1.xMax - frag2.xMax; } break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } -int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) +bool TextLineFrag::cmpXYColumnPrimaryRot(const TextLineFrag &frag1, const TextLineFrag &frag2) { - TextLineFrag *frag1 = (TextLineFrag *)p1; - TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; // if columns overlap, compare y values - if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] - frag2->line->col[frag2->start]) && frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start])) { + if (frag1.col < frag2.col + (frag2.line->col[frag2.start + frag2.len] - frag2.line->col[frag2.start]) && frag2.col < frag1.col + (frag1.line->col[frag1.start + frag1.len] - frag1.line->col[frag1.start])) { cmp = 0; // make gcc happy - switch (frag1->line->blk->page->primaryRot) { + switch (frag1.line->blk->page->primaryRot) { case 0: - cmp = frag1->yMin - frag2->yMin; + cmp = frag1.yMin - frag2.yMin; break; case 1: - cmp = frag2->xMax - frag1->xMax; + cmp = frag2.xMax - frag1.xMax; break; case 2: - cmp = frag2->yMin - frag1->yMin; + cmp = frag2.yMin - frag1.yMin; break; case 3: - cmp = frag1->xMax - frag2->xMax; + cmp = frag1.xMax - frag2.xMax; break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } // otherwise, compare starting column - return frag1->col - frag2->col; + return frag1.col - frag2.col < 0; } -int TextLineFrag::cmpXYColumnLineRot(const void *p1, const void *p2) +bool TextLineFrag::cmpXYColumnLineRot(const TextLineFrag &frag1, const TextLineFrag &frag2) { - TextLineFrag *frag1 = (TextLineFrag *)p1; - TextLineFrag *frag2 = (TextLineFrag *)p2; double cmp; // if columns overlap, compare y values - if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] - frag2->line->col[frag2->start]) && frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] - frag1->line->col[frag1->start])) { + if (frag1.col < frag2.col + (frag2.line->col[frag2.start + frag2.len] - frag2.line->col[frag2.start]) && frag2.col < frag1.col + (frag1.line->col[frag1.start + frag1.len] - frag1.line->col[frag1.start])) { cmp = 0; // make gcc happy - switch (frag1->line->rot) { + switch (frag1.line->rot) { case 0: - cmp = frag1->yMin - frag2->yMin; + cmp = frag1.yMin - frag2.yMin; break; case 1: - cmp = frag2->xMax - frag1->xMax; + cmp = frag2.xMax - frag1.xMax; break; case 2: - cmp = frag2->yMin - frag1->yMin; + cmp = frag2.yMin - frag1.yMin; break; case 3: - cmp = frag1->xMax - frag2->xMax; + cmp = frag1.xMax - frag2.xMax; break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } // otherwise, compare starting column - return frag1->col - frag2->col; + return frag1.col - frag2.col < 0; } //------------------------------------------------------------------------ @@ -1660,68 +1642,155 @@ void TextBlock::addWord(TextWord *word) void TextBlock::coalesce(const UnicodeMap *uMap, double fixedPitch) { - TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord; - TextLine *line, *line0, *line1; - int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx; - int baseIdx, bestWordBaseIdx, idx0, idx1; - double minBase, maxBase; - double fontSize, wordSpacing, delta, priDelta, secDelta; - TextLine **lineArray; - bool found, overlap; - int col1, col2; - int i, j, k; + // Sort words topologically before coalescing + pool->sort(); // discard duplicated text (fake boldface, drop shadows) - for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) { - word0 = pool->getPool(idx0); + for (int idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) { + // Get the first LHS word from the pool + TextWord *word0 = pool->getPool(idx0); + while (word0) { - priDelta = dupMaxPriDelta * word0->fontSize; - secDelta = dupMaxSecDelta * word0->fontSize; - maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); - found = false; - word1 = word2 = nullptr; // make gcc happy - for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) { - if (idx1 == idx0) { - word1 = word0; - word2 = word0->next; - } else { - word1 = nullptr; - word2 = pool->getPool(idx1); + double priDelta = dupMaxPriDelta * word0->fontSize; + double secDelta = dupMaxSecDelta * word0->fontSize; + + // Helper to determine if the second word "matches" the first one + // Finds identical words with almost identical positions, but also + // partially overlapping words. + auto keepSecond = [&priDelta, &secDelta](const TextWord &w0, const TextWord &w1) -> std::pair { + auto equalText = [](const TextWord &iw0, const TextWord &iw1, size_t len, size_t offset = 0) -> bool { // + return std::equal(iw0.chars.begin() + offset, iw0.chars.begin() + offset + len, iw1.chars.begin(), iw1.chars.begin() + len, // + [](auto c1, auto c2) { return c1.text == c2.text; }); + }; + auto matchEdges = [priDelta](const TextWord &iw0, const TextWord &iw1, size_t len, size_t offset = 0) -> bool { // + return fabs(iw0.chars[offset].edge - iw1.chars[0].edge) < priDelta // + && fabs(iw0.chars[offset + len - 1].edge - iw1.chars[len - 1].edge) < priDelta; + }; + auto matchBaseTop = [secDelta](const TextWord &iw0, const TextWord &iw1) -> bool { // + if ((iw0.rot == 0) || (iw0.rot == 2)) { + return (fabs(iw0.yMin - iw1.yMin) < secDelta && fabs(iw0.yMax - iw1.yMax) < secDelta); + } else { + return (fabs(iw0.xMin - iw1.xMin) < secDelta && fabs(iw0.xMax - iw1.xMax) < secDelta); + } + }; + + // Check if bounding boxes overlap, if not, keep both words + if (w0.xMin > w1.xMax || w0.xMax < w1.xMin || w0.yMin > w1.yMax || w0.yMax < w1.yMin) { + return { 0, w1.len() }; } - for (; word2; word1 = word2, word2 = word2->next) { - if (word2->len == word0->len && !memcmp(word2->text, word0->text, word0->len * sizeof(Unicode))) { - switch (rot) { - case 0: - case 2: - found = fabs(word0->xMin - word2->xMin) < priDelta && fabs(word0->xMax - word2->xMax) < priDelta && fabs(word0->yMin - word2->yMin) < secDelta && fabs(word0->yMax - word2->yMax) < secDelta; - break; - case 1: - case 3: - found = fabs(word0->xMin - word2->xMin) < secDelta && fabs(word0->xMax - word2->xMax) < secDelta && fabs(word0->yMin - word2->yMin) < priDelta && fabs(word0->yMax - word2->yMax) < priDelta; - break; - } + + if (w0.len() == w1.len() && equalText(w0, w1, w0.len())) { + // Identical words + if (matchEdges(w0, w1, w0.len()) && matchBaseTop(w0, w1)) { + // Discard w1 + return { 0, 0 }; + } else { + return { 0, w1.len() }; } - if (found) { - break; + } else if (w0.len() < w1.len() && equalText(w0, w1, w0.len())) { + // w0 is the prefix of w1 (e.g. if the bold word ends with a non-bold colon) + if (matchEdges(w0, w1, w0.len()) && matchBaseTop(w0, w1)) { + return { w0.len(), w1.len() }; + } + } else if (w0.len() > w1.len() && equalText(w0, w1, w1.len())) { + // w1 is the prefix of w0 (e.g. if the bold word ends with a non-bold colon) + if (matchEdges(w0, w1, w1.len()) && matchBaseTop(w0, w1)) { + return { 0, 0 }; } } - if (found) { - break; + + // Check if the tail of w0 is the head of w1, (e.g. a "quoted" bold word) + // e.g.: '"quoted' + 'quoted"' -> '"quoted" + '"' + for (size_t offset = 1; offset < w0.len(); offset++) { + auto len = std::min(w0.len() - offset, w1.len()); + if (len == 0) { + break; + } + if (equalText(w0, w1, len, offset) && matchEdges(w0, w1, len, offset)) { + return { len, w1.len() }; + } } - } - if (found) { - if (word1) { - word1->next = word2->next; + + return { 0, w1.len() }; + }; + + int maxBaseIdx = pool->getBaseIdx(word0->base + secDelta); + + for (int idx1 = idx0; idx1 <= maxBaseIdx; idx1++) { + TextWord *prevWord; + /* In case the RHS word is from the same pool as the LHS word, + * start the inner loop with the word following the LHS word. + * Otherwise, start with the second word from the subsequent pools + * - the first word is compared at the end. + */ + if (idx0 == idx1) { + prevWord = word0; } else { - pool->setPool(idx1, word2->next); + prevWord = pool->getPool(idx1); + if (!prevWord) { + continue; + } + } + TextWord *word1 = prevWord->next; + + while (word1) { + if (auto keep = keepSecond(*word0, *word1); keep.first == keep.second) { + prevWord->next = word1->next; + delete word1; + word1 = prevWord->next; + } else if (keep.first != 0) { + // Discard first part of second word + word1->chars.erase(word1->chars.begin(), word1->chars.begin() + keep.first); + if (word1->rot == 0) { + word1->xMin = word0->xMax; + } else if (word1->rot == 2) { + word1->xMax = word0->xMin; + } else if (word1->rot == 1) { + word1->yMin = word0->yMax; + } else { + word1->yMax = word0->yMin; + } + + prevWord = word1; + word1 = word1->next; + } else { + prevWord = word1; + word1 = word1->next; + } + } + + // Check the first word from each subsequent pool + if (idx0 != idx1) { + word1 = pool->getPool(idx1); + } + if (!word1) { + continue; + } + if (auto keep = keepSecond(*word0, *word1); keep.first == keep.second) { + pool->setPool(idx1, word1->next); + delete word1; + } else if (keep.first != 0) { + word1->chars.erase(word1->chars.begin(), word1->chars.begin() + keep.first); + word1->xMin = word0->xMax; } - delete word2; - } else { - word0 = word0->next; } + + word0 = word0->next; } } + TextWord *word0, *word1; + TextWord *bestWord0, *bestWord1, *lastWord; + TextLine *line, *line0, *line1; + TextLine **lineArray; + int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx; + int baseIdx, bestWordBaseIdx; + double minBase, maxBase; + double fontSize, wordSpacing, delta; + bool overlap; + int col1, col2; + int i, j, k; + // build the lines curLine = nullptr; poolMinBaseIdx = pool->minBaseIdx; @@ -1834,7 +1903,7 @@ void TextBlock::coalesce(const UnicodeMap *uMap, double fixedPitch) for (line = lines, i = 0; line; line = line->next, ++i) { lineArray[i] = line; } - qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY); + std::sort(lineArray, lineArray + nLines, &TextLine::cmpXY); // column assignment nColumns = 0; @@ -1909,7 +1978,7 @@ void TextBlock::coalesce(const UnicodeMap *uMap, double fixedPitch) } } } - gfree(lineArray); + gfree(static_cast(lineArray)); } void TextBlock::updatePriMinMax(const TextBlock *blk) @@ -1965,10 +2034,8 @@ void TextBlock::updatePriMinMax(const TextBlock *blk) } } -int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) +bool TextBlock::cmpXYPrimaryRot(const TextBlock *const blk1, const TextBlock *const blk2) { - TextBlock *blk1 = *(TextBlock **)p1; - TextBlock *blk2 = *(TextBlock **)p2; double cmp; cmp = 0; // make gcc happy @@ -1994,39 +2061,7 @@ int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) } break; } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; -} - -int TextBlock::cmpYXPrimaryRot(const void *p1, const void *p2) -{ - TextBlock *blk1 = *(TextBlock **)p1; - TextBlock *blk2 = *(TextBlock **)p2; - double cmp; - - cmp = 0; // make gcc happy - switch (blk1->page->primaryRot) { - case 0: - if ((cmp = blk1->yMin - blk2->yMin) == 0) { - cmp = blk1->xMin - blk2->xMin; - } - break; - case 1: - if ((cmp = blk2->xMax - blk1->xMax) == 0) { - cmp = blk1->yMin - blk2->yMin; - } - break; - case 2: - if ((cmp = blk2->yMin - blk1->yMin) == 0) { - cmp = blk2->xMax - blk1->xMax; - } - break; - case 3: - if ((cmp = blk1->xMax - blk2->xMax) == 0) { - cmp = blk2->yMax - blk1->yMax; - } - break; - } - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; + return cmp < 0; } int TextBlock::primaryCmp(const TextBlock *blk) const @@ -2355,8 +2390,6 @@ TextWordList::TextWordList(const TextPage *text, bool physLayout) TextBlock *blk; TextLine *line; TextWord *word; - TextWord **wordArray; - int nWords, i; if (text->rawOrder) { for (word = text->rawWords; word; word = word->next) { @@ -2366,7 +2399,7 @@ TextWordList::TextWordList(const TextPage *text, bool physLayout) } else if (physLayout) { // this is inefficient, but it's also the least useful of these // three cases - nWords = 0; + int nWords = 0; for (flow = text->flows; flow; flow = flow->next) { for (blk = flow->blocks; blk; blk = blk->next) { for (line = blk->lines; line; line = line->next) { @@ -2376,22 +2409,17 @@ TextWordList::TextWordList(const TextPage *text, bool physLayout) } } } - wordArray = (TextWord **)gmallocn(nWords, sizeof(TextWord *)); - i = 0; + words.reserve(nWords); for (flow = text->flows; flow; flow = flow->next) { for (blk = flow->blocks; blk; blk = blk->next) { for (line = blk->lines; line; line = line->next) { for (word = line->words; word; word = word->next) { - wordArray[i++] = word; + words.push_back(word); } } } } - qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX); - for (i = 0; i < nWords; ++i) { - words.push_back(wordArray[i]); - } - gfree(wordArray); + std::ranges::sort(words, &TextWord::cmpYX); } else { for (flow = text->flows; flow; flow = flow->next) { @@ -2406,7 +2434,7 @@ TextWordList::TextWordList(const TextPage *text, bool physLayout) } } -TextWordList::~TextWordList() { } +TextWordList::~TextWordList() = default; int TextWordList::getLength() const { @@ -2489,6 +2517,11 @@ void TextPage::endPage() if (curWord) { endWord(); } + for (std::unique_ptr &pool : pools) { + if (pool) { + pool->sort(); + } + } } void TextPage::clear() @@ -2516,7 +2549,7 @@ void TextPage::clear() flows = flows->next; delete flow; } - gfree(blocks); + gfree(static_cast(blocks)); } fonts.clear(); underlines.clear(); @@ -2613,7 +2646,7 @@ void TextPage::beginWord(const GfxState *state) // compute the rotation state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]); - std::shared_ptr gfxFont = state->getFont(); + const std::shared_ptr &gfxFont = state->getFont(); if (gfxFont && gfxFont->getType() == fontType3) { fontm = state->getFont()->getFontMatrix(); m2[0] = fontm[0] * m[0] + fontm[1] * m[2]; @@ -2711,28 +2744,28 @@ void TextPage::addChar(const GfxState *state, double x, double y, double dx, dou // character to be in a word by itself at this stage), // (4) the font size has changed // (5) the WMode changed - if (curWord && curWord->len > 0) { + if (curWord && curWord->len() > 0) { base = sp = delta = 0; // make gcc happy switch (curWord->rot) { case 0: base = y1; sp = x1 - curWord->xMax; - delta = x1 - curWord->edge[curWord->len - 1]; + delta = x1 - curWord->chars.back().edge; break; case 1: base = x1; sp = y1 - curWord->yMax; - delta = y1 - curWord->edge[curWord->len - 1]; + delta = y1 - curWord->chars.back().edge; break; case 2: base = y1; sp = curWord->xMin - x1; - delta = curWord->edge[curWord->len - 1] - x1; + delta = curWord->chars.back().edge - x1; break; case 3: base = x1; sp = curWord->yMin - y1; - delta = curWord->edge[curWord->len - 1] - y1; + delta = curWord->chars.back().edge - y1; break; } overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize && fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize; @@ -2811,7 +2844,7 @@ void TextPage::addWord(TextWord *word) { // throw away zero-length words -- they don't have valid xMin/xMax // values, and they're useless anyway - if (word->len == 0) { + if (word->len() == 0) { delete word; return; } @@ -3307,10 +3340,10 @@ void TextPage::coalesce(bool physLayout, double fixedPitch, bool doHTML, double for (blk = blkList; blk; blk = blk->next) { for (line = blk->lines; line; line = line->next) { for (word0 = line->words; word0; word0 = word0->next) { - for (int i = 0; i < word0->len; ++i) { - if (unicodeTypeL(word0->text[i])) { + for (size_t i = 0; i < word0->len(); ++i) { + if (unicodeTypeL(word0->chars[i].text)) { ++lrCount; - } else if (unicodeTypeR(word0->text[i])) { + } else if (unicodeTypeR(word0->chars[i].text)) { --lrCount; } } @@ -3329,7 +3362,7 @@ void TextPage::coalesce(bool physLayout, double fixedPitch, bool doHTML, double // sort blocks into xy order for column assignment if (blocks) { - gfree(blocks); + gfree(static_cast(blocks)); } if (physLayout && fixedPitch) { @@ -3368,9 +3401,7 @@ void TextPage::coalesce(bool physLayout, double fixedPitch, bool doHTML, double for (blk = blkList, i = 0; blk; blk = blk->next, ++i) { blocks[i] = blk; } - if (blocks) { - qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot); - } + std::sort(blocks, blocks + nBlocks, &TextBlock::cmpXYPrimaryRot); // column assignment for (i = 0; i < nBlocks; ++i) { @@ -3858,7 +3889,8 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB TextLine *line; Unicode *s2, *txt, *reordered; Unicode *p; - Unicode *nextline; + TextLine *nextline; + Unicode *nextline_txt; int nextline_len; bool nextlineAfterHyphen = false; int txtSize, m, i, j, k; @@ -3967,18 +3999,29 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB line->normalized = unicodeNormalizeNFKC(line->text, line->len, &line->normalized_len, &line->normalized_idx, true); } - if (matchAcrossLines && line->next && !line->next->normalized) { - line->next->normalized = unicodeNormalizeNFKC(line->next->text, line->next->len, &line->next->normalized_len, &line->next->normalized_idx, true); - } nextline = nullptr; + nextline_txt = nullptr; nextline_len = 0; + if (line->next) { + nextline = line->next; + } else { + // set nextline to first line of next block + int ind = i + (backward ? -1 : 1); + if ((backward && ind >= 0) || (!backward && ind < nBlocks)) { + nextline = blocks[ind]->lines; + } + } + + if (matchAcrossLines && nextline && !nextline->normalized) { + nextline->normalized = unicodeNormalizeNFKC(nextline->text, nextline->len, &nextline->normalized_len, &nextline->normalized_idx, true); + } // convert the line to uppercase m = line->normalized_len; if (ignoreDiacritics) { if (!line->ascii_translation) { - unicodeToAscii7(line->normalized, line->normalized_len, &line->ascii_translation, &line->ascii_len, line->normalized_idx, &line->ascii_idx); + unicodeToAscii7(std::span(line->normalized, line->normalized_len), &line->ascii_translation, &line->ascii_len, line->normalized_idx, &line->ascii_idx); } if (line->ascii_len) { m = line->ascii_len; @@ -3986,8 +4029,8 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB ignoreDiacritics = false; } - if (matchAcrossLines && line->next && !line->next->ascii_translation) { - unicodeToAscii7(line->next->normalized, line->next->normalized_len, &line->next->ascii_translation, &line->next->ascii_len, line->next->normalized_idx, &line->next->ascii_idx); + if (matchAcrossLines && nextline && !nextline->ascii_translation) { + unicodeToAscii7(std::span(nextline->normalized, nextline->normalized_len), &nextline->ascii_translation, &nextline->ascii_len, nextline->normalized_idx, &nextline->ascii_idx); } } if (!caseSensitive) { @@ -4002,11 +4045,11 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB txt[k] = unicodeToUpper(line->normalized[k]); } } - if (matchAcrossLines && line->next) { - nextline_len = ignoreDiacritics ? line->next->ascii_len : line->next->normalized_len; - nextline = (Unicode *)gmallocn(nextline_len, sizeof(Unicode)); + if (matchAcrossLines && nextline) { + nextline_len = ignoreDiacritics ? nextline->ascii_len : nextline->normalized_len; + nextline_txt = (Unicode *)gmallocn(nextline_len, sizeof(Unicode)); for (k = 0; k < nextline_len; ++k) { - nextline[k] = ignoreDiacritics ? unicodeToUpper(line->next->ascii_translation[k]) : unicodeToUpper(line->next->normalized[k]); + nextline_txt[k] = ignoreDiacritics ? unicodeToUpper(nextline->ascii_translation[k]) : unicodeToUpper(nextline->normalized[k]); } } } else { @@ -4016,20 +4059,20 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB txt = line->normalized; } - if (matchAcrossLines && line->next) { - nextline_len = ignoreDiacritics ? line->next->ascii_len : line->next->normalized_len; - nextline = ignoreDiacritics ? line->next->ascii_translation : line->next->normalized; + if (matchAcrossLines && nextline) { + nextline_len = ignoreDiacritics ? nextline->ascii_len : nextline->normalized_len; + nextline_txt = ignoreDiacritics ? nextline->ascii_translation : nextline->normalized; } } // search each position in this line j = backward ? m - len : 0; p = txt + j; - while (backward ? j >= 0 : j <= m - (nextline ? 1 : len)) { + while (backward ? j >= 0 : j <= m - (nextline_txt ? 1 : len)) { bool wholeWordStartIsOk, wholeWordEndIsOk; if (wholeWord) { wholeWordStartIsOk = j == 0 || !unicodeTypeAlphaNum(txt[j - 1]); - if (nextline) { + if (nextline_txt) { wholeWordEndIsOk = true; // word end may be in next line, so we'll check it later } else { wholeWordEndIsOk = j + len == m || !unicodeTypeAlphaNum(txt[j + len]); @@ -4046,7 +4089,7 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB bool last_char_of_search_term = k == len - 1; bool match_started = (bool)k; - if (p[k] != s2[k] || (nextline && last_char_of_line && !last_char_of_search_term)) { + if (p[k] != s2[k] || (nextline_txt && last_char_of_line && !last_char_of_search_term)) { // now check if the comparison failed at the end-of-line hyphen, // and if so, keep on comparing at the next line nextlineAfterHyphen = false; @@ -4063,7 +4106,7 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB } for (; n < nextline_len && k < len; ++k, ++n) { - if (nextline[n] != s2[k]) { + if (nextline_txt[n] != s2[k]) { if (!spaceConsumedByNewline && !n && UnicodeIsWhitespace(s2[k])) { n = -1; spaceConsumedByNewline = true; @@ -4077,9 +4120,9 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB } found_it = k == len; - if (found_it && nextline && wholeWord) { // check word end for nextline case + if (found_it && nextline_txt && wholeWord) { // check word end for nextline case if (n) { // Match ended at next line - wholeWordEndIsOk = n == nextline_len || !unicodeTypeAlphaNum(nextline[n]); + wholeWordEndIsOk = n == nextline_len || !unicodeTypeAlphaNum(nextline_txt[n]); } else { // Match ended on same line wholeWordEndIsOk = j + len == m || !unicodeTypeAlphaNum(txt[j + len]); } @@ -4100,14 +4143,14 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB int normStart, normAfterEnd; if (ignoreDiacritics) { normStart = line->ascii_idx[j]; - if (nextline) { + if (nextline_txt) { normAfterEnd = line->ascii_idx[j + k - n]; } else { normAfterEnd = line->ascii_idx[j + len - 1] + 1; } } else { normStart = line->normalized_idx[j]; - if (nextline) { + if (nextline_txt) { normAfterEnd = line->normalized_idx[j + k - n]; } else { normAfterEnd = line->normalized_idx[j + len - 1] + 1; @@ -4140,7 +4183,7 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB } if (continueMatch) { - adjustRotation(line->next, 0, n, &xMin2, &xMax2, &yMin2, &yMax2); + adjustRotation(nextline, 0, n, &xMin2, &xMax2, &yMin2, &yMax2); continueMatch->x1 = xMin2; continueMatch->y1 = yMax2; continueMatch->x2 = xMax2; @@ -4167,8 +4210,8 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB } } - if (nextline && nextline != line->next->ascii_translation && nextline != line->next->normalized) { - gfree(nextline); + if (nextline_txt && nextline_txt != nextline->ascii_translation && nextline_txt != nextline->normalized) { + gfree(nextline_txt); } } } @@ -4193,23 +4236,13 @@ bool TextPage::findText(const Unicode *s, int len, bool startAtTop, bool stopAtB return false; } -GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, EndOfLineKind textEOL) const +GooString TextPage::getText(double xMin, double yMin, double xMax, double yMax, EndOfLineKind textEOL) const { - GooString *s; const UnicodeMap *uMap; - TextBlock *blk; - TextLine *line; - TextLineFrag *frags; - int nFrags, fragsSize; - TextLineFrag *frag; char space[8], eol[16]; int spaceLen, eolLen; - int lastRot; - double x, y, delta; - int col, idx0, idx1, i, j; - bool multiLine, oneRot; - s = new GooString(); + GooString s; // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { @@ -4217,17 +4250,16 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, } if (rawOrder) { - TextWord *word; char mbc[16]; int mbc_len; - for (word = rawWords; word && word <= rawLastWord; word = word->next) { - for (j = 0; j < word->getLength(); ++j) { + for (TextWord *word = rawWords; word && word <= rawLastWord; word = word->next) { + for (int j = 0; j < word->getLength(); ++j) { double gXMin, gXMax, gYMin, gYMax; word->getCharBBox(j, &gXMin, &gYMin, &gXMax, &gYMax); if (xMin <= gXMin && gXMax <= xMax && yMin <= gYMin && gYMax <= yMax) { mbc_len = uMap->mapUnicode(*(word->getChar(j)), mbc, sizeof(mbc)); - s->append(mbc, mbc_len); + s.append(mbc, mbc_len); } } } @@ -4252,22 +4284,23 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, //~ writing mode (horiz/vert) // collect the line fragments that are in the rectangle - fragsSize = 256; - frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag)); - nFrags = 0; - lastRot = -1; - oneRot = true; - for (i = 0; i < nBlocks; ++i) { - blk = blocks[i]; + std::vector frags; + frags.reserve(256); + int lastRot = -1; + bool oneRot = true; + for (int i = 0; i < nBlocks; ++i) { + TextBlock *blk = blocks[i]; if (xMin < blk->xMax && blk->xMin < xMax && yMin < blk->yMax && blk->yMin < yMax) { - for (line = blk->lines; line; line = line->next) { + for (TextLine *line = blk->lines; line; line = line->next) { if (xMin < line->xMax && line->xMin < xMax && yMin < line->yMax && line->yMin < yMax) { + double y = 0.5 * (line->yMin + line->yMax); + double x = 0.5 * (line->xMin + line->xMax); + int idx0, idx1; idx0 = idx1 = -1; switch (line->rot) { case 0: - y = 0.5 * (line->yMin + line->yMax); if (yMin < y && y < yMax) { - j = 0; + int j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > xMin) { idx0 = j; @@ -4286,9 +4319,8 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, } break; case 1: - x = 0.5 * (line->xMin + line->xMax); if (xMin < x && x < xMax) { - j = 0; + int j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) > yMin) { idx0 = j; @@ -4307,9 +4339,8 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, } break; case 2: - y = 0.5 * (line->yMin + line->yMax); if (yMin < y && y < yMax) { - j = 0; + int j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < xMax) { idx0 = j; @@ -4328,9 +4359,8 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, } break; case 3: - x = 0.5 * (line->xMin + line->xMax); if (xMin < x && x < xMax) { - j = 0; + int j = 0; while (j < line->len) { if (0.5 * (line->edge[j] + line->edge[j + 1]) < yMax) { idx0 = j; @@ -4350,12 +4380,8 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, break; } if (idx0 >= 0 && idx1 >= 0) { - if (nFrags == fragsSize) { - fragsSize *= 2; - frags = (TextLineFrag *)greallocn(frags, fragsSize, sizeof(TextLineFrag)); - } - frags[nFrags].init(line, idx0, idx1 - idx0 + 1); - ++nFrags; + frags.push_back({}); + frags.back().init(line, idx0, idx1 - idx0 + 1); if (lastRot >= 0 && line->rot != lastRot) { oneRot = false; } @@ -4367,58 +4393,56 @@ GooString *TextPage::getText(double xMin, double yMin, double xMax, double yMax, } // sort the fragments and generate the string - if (nFrags > 0) { - - for (i = 0; i < nFrags; ++i) { - frags[i].computeCoords(oneRot); + if (!frags.empty()) { + for (auto &frag : frags) { + frag.computeCoords(oneRot); } - assignColumns(frags, nFrags, oneRot); + assignColumns(frags.data(), frags.size(), oneRot); // if all lines in the region have the same rotation, use it; // otherwise, use the page's primary rotation if (oneRot) { - qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXLineRot); + std::ranges::sort(frags, &TextLineFrag::cmpYXLineRot); } else { - qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot); + std::ranges::sort(frags, &TextLineFrag::cmpYXPrimaryRot); } - i = 0; - while (i < nFrags) { - delta = maxIntraLineDelta * frags[i].line->words->fontSize; - for (j = i + 1; j < nFrags && fabs(frags[j].base - frags[i].base) < delta; ++j) { - ; - } - qsort(frags + i, j - i, sizeof(TextLineFrag), oneRot ? &TextLineFrag::cmpXYColumnLineRot : &TextLineFrag::cmpXYColumnPrimaryRot); - i = j; + for (auto it = frags.begin(); it != frags.end();) { + double delta = maxIntraLineDelta * it->line->words->fontSize; + double base = it->base; + + auto end = std::find_if(it + 1, frags.end(), [base, delta](const TextLineFrag &frag) { // + return fabs(frag.base - base) >= delta; + }); + std::sort(it, end, oneRot ? &TextLineFrag::cmpXYColumnLineRot : &TextLineFrag::cmpXYColumnPrimaryRot); + it = end; } - col = 0; - multiLine = false; - for (i = 0; i < nFrags; ++i) { - frag = &frags[i]; + int col = 0; + bool multiLine = false; + for (size_t i = 0; i < frags.size(); ++i) { + TextLineFrag *frag = &frags[i]; // insert a return if (frag->col < col || (i > 0 && fabs(frag->base - frags[i - 1].base) > maxIntraLineDelta * frags[i - 1].line->words->fontSize)) { - s->append(eol, eolLen); + s.append(eol, eolLen); col = 0; multiLine = true; } // column alignment for (; col < frag->col; ++col) { - s->append(space, spaceLen); + s.append(space, spaceLen); } // get the fragment text - col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s); + col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, &s); } if (multiLine) { - s->append(eol, eolLen); + s.append(eol, eolLen); } } - gfree(frags); - return s; } @@ -4447,71 +4471,49 @@ class TextSelectionDumper : public TextSelectionVisitor explicit TextSelectionDumper(TextPage *page); ~TextSelectionDumper() override; - void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override {}; + void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override { }; void visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) override; void visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) override; void endPage(); - GooString *getText(); - std::vector **takeWordList(int *nLines); + GooString getText(); + std::vector>> takeWordList(); private: void startLine(); void finishLine(); - std::vector **lines; - int nLines, linesSize; - std::vector *words; + std::vector>> lines; + std::vector> words; int tableId; TextBlock *currentBlock; }; TextSelectionDumper::TextSelectionDumper(TextPage *p) : TextSelectionVisitor(p) { - linesSize = 256; - lines = (std::vector **)gmallocn(linesSize, sizeof(std::vector *)); - nLines = 0; - tableId = -1; currentBlock = nullptr; - words = nullptr; } -TextSelectionDumper::~TextSelectionDumper() -{ - for (int i = 0; i < nLines; i++) { - for (auto entry : *(lines[i])) { - delete entry; - } - delete lines[i]; - } - gfree(lines); -} +TextSelectionDumper::~TextSelectionDumper() = default; void TextSelectionDumper::startLine() { finishLine(); - words = new std::vector(); + words.clear(); } void TextSelectionDumper::finishLine() { - if (nLines == linesSize) { - linesSize *= 2; - lines = (std::vector **)grealloc(lines, linesSize * sizeof(std::vector *)); - } - - if (words && words->size() > 0) { + if (!words.empty()) { // Reverse word order for RTL text. Fixes #53 for glib backend (Evince) if (!page->primaryLR) { - std::reverse(words->begin(), words->end()); + std::ranges::reverse(words); } - lines[nLines++] = words; - } else if (words) { - delete words; + lines.push_back(std::move(words)); } - words = nullptr; + words.clear(); } void TextSelectionDumper::visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) @@ -4548,7 +4550,7 @@ void TextSelectionDumper::visitLine(TextLine *line, TextWord *begin, TextWord *e void TextSelectionDumper::visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) { - words->push_back(new TextWordSelection(word, begin, end)); + words.push_back(std::make_unique(word, begin, end)); } void TextSelectionDumper::endPage() @@ -4556,16 +4558,13 @@ void TextSelectionDumper::endPage() finishLine(); } -GooString *TextSelectionDumper::getText() +GooString TextSelectionDumper::getText() { - GooString *text; - int i; + GooString text; const UnicodeMap *uMap; char space[8], eol[16]; int spaceLen, eolLen; - text = new GooString(); - if (!(uMap = globalParams->getTextEncoding())) { return text; } @@ -4573,36 +4572,32 @@ GooString *TextSelectionDumper::getText() spaceLen = uMap->mapUnicode(0x20, space, sizeof(space)); eolLen = uMap->mapUnicode(0x0a, eol, sizeof(eol)); - for (i = 0; i < nLines; i++) { - std::vector *lineWords = lines[i]; - for (std::size_t j = 0; j < lineWords->size(); j++) { - TextWordSelection *sel = (*lineWords)[j]; + std::vector uText; + for (size_t i = 0; i < lines.size(); i++) { + const auto &line = lines[i]; + for (size_t j = 0; j < line.size(); j++) { + const auto &sel = line[j]; + + uText.resize(sel->end - sel->begin); + std::transform(sel->word->chars.begin() + sel->begin, sel->word->chars.begin() + sel->end, uText.begin(), [](auto &c) { return c.text; }); + page->dumpFragment(uText.data(), uText.size(), uMap, &text); - page->dumpFragment(sel->word->text + sel->begin, sel->end - sel->begin, uMap, text); - if (j < lineWords->size() - 1 && sel->word->spaceAfter) { - text->append(space, spaceLen); + if (j < line.size() - 1 && sel->word->spaceAfter) { + text.append(space, spaceLen); } } - if (i < nLines - 1) { - text->append(eol, eolLen); + if (i < lines.size() - 1) { + text.append(eol, eolLen); } } return text; } -std::vector **TextSelectionDumper::takeWordList(int *nLinesOut) +std::vector>> TextSelectionDumper::takeWordList() { - std::vector **returnValue = lines; - - *nLinesOut = nLines; - if (nLines == 0) { - return nullptr; - } - - nLines = 0; - lines = nullptr; - + std::vector>> returnValue; + std::swap(lines, returnValue); return returnValue; } @@ -4612,9 +4607,9 @@ class TextSelectionSizer : public TextSelectionVisitor TextSelectionSizer(TextPage *page, double scale); ~TextSelectionSizer() override { delete list; } - void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override {}; + void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override { }; void visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) override; - void visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) override {}; + void visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) override { }; std::vector *takeRegion() { @@ -4680,7 +4675,7 @@ class TextSelectionPainter : public TextSelectionVisitor TextSelectionPainter(TextPage *page, double scale, int rotation, OutputDev *out, const GfxColor *box_color, const GfxColor *glyph_color); ~TextSelectionPainter() override; - void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override {}; + void visitBlock(TextBlock *block, TextLine *begin, TextLine *end, const PDFRectangle *selection) override { }; void visitLine(TextLine *line, TextWord *begin, TextWord *end, int edge_begin, int edge_end, const PDFRectangle *selection) override; void visitWord(TextWord *word, int begin, int end, const PDFRectangle *selection) override; void endPage(); @@ -4707,7 +4702,7 @@ TextSelectionPainter::TextSelectionPainter(TextPage *p, double scale, int rotati out->startPage(0, state, nullptr); out->setDefaultCTM(state->getCTM()); - state->setFillColorSpace(new GfxDeviceRGBColorSpace()); + state->setFillColorSpace(std::make_unique()); state->setFillColor(box_color); out->updateFillColor(state); } @@ -4793,7 +4788,7 @@ void TextSelectionPainter::visitWord(TextWord *word, int begin, int end, const P bool TextSelectionPainter::hasGlyphLessFont() { - if (selectionList && selectionList->size()) { + if (selectionList && !selectionList->empty()) { TextWordSelection *sel = (*selectionList)[0]; return sel->word->invisible; } @@ -4803,6 +4798,16 @@ bool TextSelectionPainter::hasGlyphLessFont() void TextSelectionPainter::endPage() { + /* Take a shortcut for glyphless fonts (eg. Tesseract scanned documents) + * cause we just paint a transparent fill over existent text.Issue #157 */ + if (hasGlyphLessFont()) { + state->setFillOpacity(glyphlessSelectionOpacity); + out->updateFillOpacity(state); + out->fill(state); + out->endPage(); + return; + } + out->fill(state); out->saveState(state); @@ -4812,46 +4817,41 @@ void TextSelectionPainter::endPage() state->setFillColor(glyph_color); - bool usingGlyphLessFont = hasGlyphLessFont(); - /* Paint transparent selection when using tesseract glyphless font. Issue #157 */ - if (usingGlyphLessFont) { - state->setFillOpacity(glyphlessSelectionOpacity); - } - out->updateFillColor(state); + GooString string; for (const TextWordSelection *sel : *selectionList) { int begin = sel->begin; while (begin < sel->end) { - TextFontInfo *font = sel->word->font[begin]; - Matrix *mat = &sel->word->textMat[begin]; + TextFontInfo *font = sel->word->chars[begin].font; + const Matrix *mat = &sel->word->chars[begin].textMat; state->setTextMat(mat->m[0], mat->m[1], mat->m[2], mat->m[3], 0, 0); state->setFont(font->gfxFont, 1); out->updateFont(state); int fEnd = begin + 1; - while (fEnd < sel->end && font->matches(sel->word->font[fEnd]) && mat->m[0] == sel->word->textMat[fEnd].m[0] && mat->m[1] == sel->word->textMat[fEnd].m[1] && mat->m[2] == sel->word->textMat[fEnd].m[2] - && mat->m[3] == sel->word->textMat[fEnd].m[3]) { + while (fEnd < sel->end && font->matches(sel->word->chars[fEnd].font) // + && mat->m[0] == sel->word->chars[fEnd].textMat.m[0] && mat->m[1] == sel->word->chars[fEnd].textMat.m[1] // + && mat->m[2] == sel->word->chars[fEnd].textMat.m[2] && mat->m[3] == sel->word->chars[fEnd].textMat.m[3]) { fEnd++; } /* The only purpose of this string is to let the output device query * it's length. Might want to change this interface later. */ - GooString *string = new GooString((char *)sel->word->charcode, fEnd - begin); - out->beginString(state, string); + string.clear(); + std::for_each(sel->word->chars.begin() + begin, sel->word->chars.begin() + fEnd, [&string](const auto c) { string.append(c.charcode); }); + out->beginString(state, &string); - if (!usingGlyphLessFont) { - for (int j = begin; j < fEnd; j++) { - if (j != begin && sel->word->charPos[j] == sel->word->charPos[j - 1]) { - continue; - } - out->drawChar(state, sel->word->textMat[j].m[4], sel->word->textMat[j].m[5], 0, 0, 0, 0, sel->word->charcode[j], 1, nullptr, 0); + for (int j = begin; j < fEnd; j++) { + const auto &charJ = sel->word->chars[j]; + if (j != begin && charJ.charPos == sel->word->chars[j - 1].charPos) { + continue; } + out->drawChar(state, charJ.textMat.m[4], charJ.textMat.m[5], 0, 0, 0, 0, charJ.charcode, 1, nullptr, 0); } out->endString(state); - delete string; begin = fEnd; } } @@ -4862,7 +4862,6 @@ void TextSelectionPainter::endPage() void TextWord::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style) { - int i, begin, end; double mid, s1, s2; if (rot == 0 || rot == 2) { @@ -4873,10 +4872,14 @@ void TextWord::visitSelection(TextSelectionVisitor *visitor, const PDFRectangle s2 = selection->y2; } - begin = len; - end = 0; - for (i = 0; i < len; i++) { - mid = (edge[i] + edge[i + 1]) / 2; + size_t begin = len(); + size_t end = 0; + for (size_t i = 0; i < len(); i++) { + if (i + 1 < len()) { + mid = (chars[i].edge + chars[i + 1].edge) / 2; + } else { + mid = (chars[i].edge + edgeEnd) / 2; + } if (XBetweenAB(mid, s1, s2)) { if (i < begin) { begin = i; @@ -5235,7 +5238,7 @@ std::vector *TextPage::getSelectionRegion(const PDFRectangle *se return sizer.takeRegion(); } -GooString *TextPage::getSelectionText(const PDFRectangle *selection, SelectionStyle style) +GooString TextPage::getSelectionText(const PDFRectangle *selection, SelectionStyle style) { TextSelectionDumper dumper(this); @@ -5245,14 +5248,14 @@ GooString *TextPage::getSelectionText(const PDFRectangle *selection, SelectionSt return dumper.getText(); } -std::vector **TextPage::getSelectionWords(const PDFRectangle *selection, SelectionStyle style, int *nLines) +std::vector>> TextPage::getSelectionWords(const PDFRectangle *selection, SelectionStyle style) { TextSelectionDumper dumper(this); visitSelection(&dumper, selection, style); dumper.endPage(); - return dumper.takeWordList(nLines); + return dumper.takeWordList(); } bool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, double *xMax, double *yMax) const @@ -5263,7 +5266,6 @@ bool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, do double xMin0, xMax0, yMin0, yMax0; double xMin1, xMax1, yMin1, yMax1; bool first; - int i, j0, j1; if (rawOrder) { return false; @@ -5275,41 +5277,44 @@ bool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, do first = true; xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy - for (i = 0; i < nBlocks; ++i) { + for (int i = 0; i < nBlocks; ++i) { blk = blocks[i]; for (line = blk->lines; line; line = line->next) { for (word = line->words; word; word = word->next) { - if (pos < word->charPos[word->len] && pos + length > word->charPos[0]) { - for (j0 = 0; j0 < word->len && pos >= word->charPos[j0 + 1]; ++j0) { + if (pos < word->charPosEnd && pos + length > word->chars.front().charPos) { + size_t j0, j1; + for (j0 = 0; (j0 + 1) < word->len() && pos >= word->chars[j0 + 1].charPos; ++j0) { ; } - for (j1 = word->len - 1; j1 > j0 && pos + length <= word->charPos[j1]; --j1) { + for (j1 = word->len(); j1 > j0 && pos + length <= word->chars[j1].charPos; --j1) { ; } + auto startingEdge = word->chars[j0].edge; + auto endingEdge = (j1 + 1 == word->len()) ? word->edgeEnd : word->chars[j1 + 1].edge; switch (line->rot) { case 0: - xMin1 = word->edge[j0]; - xMax1 = word->edge[j1 + 1]; + xMin1 = startingEdge; + xMax1 = endingEdge; yMin1 = word->yMin; yMax1 = word->yMax; break; case 1: xMin1 = word->xMin; xMax1 = word->xMax; - yMin1 = word->edge[j0]; - yMax1 = word->edge[j1 + 1]; + yMin1 = startingEdge; + yMax1 = endingEdge; break; case 2: - xMin1 = word->edge[j1 + 1]; - xMax1 = word->edge[j0]; + xMin1 = endingEdge; + xMax1 = startingEdge; yMin1 = word->yMin; yMax1 = word->yMax; break; case 3: xMin1 = word->xMin; xMax1 = word->xMax; - yMin1 = word->edge[j1 + 1]; - yMax1 = word->edge[j0]; + yMin1 = endingEdge; + yMax1 = startingEdge; break; } if (first || xMin1 < xMin0) { @@ -5342,18 +5347,8 @@ bool TextPage::findCharRange(int pos, int length, double *xMin, double *yMin, do void TextPage::dump(void *outputStream, TextOutputFunc outputFunc, bool physLayout, EndOfLineKind textEOL, bool pageBreaks) { const UnicodeMap *uMap; - TextFlow *flow; - TextBlock *blk; - TextLine *line; - TextLineFrag *frags; - TextWord *word; - int nFrags, fragsSize; - TextLineFrag *frag; char space[8], eol[16], eop[8]; int spaceLen, eolLen, eopLen; - GooString *s; - double delta; - int col, i, j, d, n; // get the output encoding if (!(uMap = globalParams->getTextEncoding())) { @@ -5380,11 +5375,16 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc, bool physLayo // output the page in raw (content stream) order if (rawOrder) { - for (word = rawWords; word; word = word->next) { - s = new GooString(); - dumpFragment(word->text, word->len, uMap, s); - (*outputFunc)(outputStream, s->c_str(), s->getLength()); - delete s; + GooString s; + std::vector uText; + + for (TextWord *word = rawWords; word; word = word->next) { + s.clear(); + uText.resize(word->len()); + std::ranges::transform(word->chars, uText.begin(), [](auto &c) { return c.text; }); + dumpFragment(uText.data(), uText.size(), uMap, &s); + (*outputFunc)(outputStream, s.c_str(), s.getLength()); + if (word->next && fabs(word->next->base - word->base) < maxIntraLineDelta * word->fontSize && word->next->xMin > word->xMax - minDupBreakOverlap * word->fontSize) { if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) { (*outputFunc)(outputStream, space, spaceLen); @@ -5398,74 +5398,63 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc, bool physLayo } else if (physLayout) { // collect the line fragments for the page and sort them - fragsSize = 256; - frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag)); - nFrags = 0; - for (i = 0; i < nBlocks; ++i) { - blk = blocks[i]; - for (line = blk->lines; line; line = line->next) { - if (nFrags == fragsSize) { - fragsSize *= 2; - frags = (TextLineFrag *)greallocn(frags, fragsSize, sizeof(TextLineFrag)); - } - frags[nFrags].init(line, 0, line->len); - frags[nFrags].computeCoords(true); - ++nFrags; + std::vector frags; + frags.reserve(256); + for (int i = 0; i < nBlocks; ++i) { + TextBlock *blk = blocks[i]; + for (TextLine *line = blk->lines; line; line = line->next) { + frags.push_back({}); + frags.back().init(line, 0, line->len); + frags.back().computeCoords(true); } } - qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot); - i = 0; - while (i < nFrags) { - delta = maxIntraLineDelta * frags[i].line->words->fontSize; - for (j = i + 1; j < nFrags && fabs(frags[j].base - frags[i].base) < delta; ++j) { - ; - } - qsort(frags + i, j - i, sizeof(TextLineFrag), &TextLineFrag::cmpXYColumnPrimaryRot); - i = j; + std::ranges::sort(frags, &TextLineFrag::cmpYXPrimaryRot); + for (auto it = frags.begin(); it != frags.end();) { + double delta = maxIntraLineDelta * it->line->words->fontSize; + double base = it->base; + + auto end = std::find_if(it + 1, frags.end(), [base, delta](const TextLineFrag &frag) { // + return fabs(frag.base - base) >= delta; + }); + std::sort(it, end, &TextLineFrag::cmpXYColumnPrimaryRot); + it = end; } #if 0 // for debugging printf("*** line fragments ***\n"); - for (i = 0; i < nFrags; ++i) { - frag = &frags[i]; + for (const auto& frag : frags) { printf("frag: x=%.2f..%.2f y=%.2f..%.2f base=%.2f '", - frag->xMin, frag->xMax, frag->yMin, frag->yMax, frag->base); - for (n = 0; n < frag->len; ++n) { - fputc(frag->line->text[frag->start + n] & 0xff, stdout); + frag.xMin, frag.xMax, frag.yMin, frag.yMax, frag.base); + for (int n = 0; n < frag.len; ++n) { + fputc(frag.line->text[frag.start + n] & 0xff, stdout); } printf("'\n"); } printf("\n"); #endif + GooString s; // generate output - col = 0; - for (i = 0; i < nFrags; ++i) { - frag = &frags[i]; + int col = 0; + for (size_t i = 0; i < frags.size(); ++i) { + const auto &frag = frags[i]; // column alignment - for (; col < frag->col; ++col) { + for (; col < frag.col; ++col) { (*outputFunc)(outputStream, space, spaceLen); } // print the line - s = new GooString(); - col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s); - (*outputFunc)(outputStream, s->c_str(), s->getLength()); - delete s; + s.clear(); + col += dumpFragment(frag.line->text + frag.start, frag.len, uMap, &s); + (*outputFunc)(outputStream, s.c_str(), s.getLength()); // print one or more returns if necessary - if (i == nFrags - 1 || frags[i + 1].col < col || fabs(frags[i + 1].base - frag->base) > maxIntraLineDelta * frag->line->words->fontSize) { - if (i < nFrags - 1) { - d = (int)((frags[i + 1].base - frag->base) / frag->line->words->fontSize); - if (d < 1) { - d = 1; - } else if (d > 5) { - d = 5; - } - } else { - d = 1; - } + if (i == frags.size() - 1) { + (*outputFunc)(outputStream, eol, eolLen); + } else if (frags[i + 1].col < col || fabs(frags[i + 1].base - frag.base) > maxIntraLineDelta * frag.line->words->fontSize) { + int d = (int)((frags[i + 1].base - frag.base) / frag.line->words->fontSize); + d = std::clamp(d, 1, 5); for (; d > 0; --d) { (*outputFunc)(outputStream, eol, eolLen); } @@ -5473,21 +5462,19 @@ void TextPage::dump(void *outputStream, TextOutputFunc outputFunc, bool physLayo } } - gfree(frags); - // output the page, "undoing" the layout } else { - for (flow = flows; flow; flow = flow->next) { - for (blk = flow->blocks; blk; blk = blk->next) { - for (line = blk->lines; line; line = line->next) { - n = line->len; + GooString s; + for (TextFlow *flow = flows; flow; flow = flow->next) { + for (TextBlock *blk = flow->blocks; blk; blk = blk->next) { + for (TextLine *line = blk->lines; line; line = line->next) { + int n = line->len; if (line->hyphenated && (line->next || blk->next)) { --n; } - s = new GooString(); - dumpFragment(line->text, n, uMap, s); - (*outputFunc)(outputStream, s->c_str(), s->getLength()); - delete s; + s.clear(); + dumpFragment(line->text, n, uMap, &s); + (*outputFunc)(outputStream, s.c_str(), s.getLength()); // output a newline when a hyphen is not suppressed if (n == line->len) { (*outputFunc)(outputStream, eol, eolLen); @@ -5517,7 +5504,7 @@ void TextPage::assignColumns(TextLineFrag *frags, int nFrags, bool oneRot) const // all text in the region has the same rotation -- recompute the // column numbers based only on the text in the region if (oneRot) { - qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpXYLineRot); + std::sort(frags, frags + nFrags, &TextLineFrag::cmpXYLineRot); rot = frags[0].line->rot; for (i = 0; i < nFrags; ++i) { frag0 = &frags[i]; @@ -5630,9 +5617,7 @@ ActualText::ActualText(TextPage *out) ActualText::~ActualText() { - if (actualText) { - delete actualText; - } + delete actualText; text->decRefCnt(); } @@ -5655,9 +5640,7 @@ void ActualText::addChar(const GfxState *state, double x, double y, double dx, d void ActualText::begin(const GfxState *state, const GooString *t) { - if (actualText) { - delete actualText; - } + delete actualText; actualText = new GooString(t); actualTextNBytes = 0; } @@ -5668,15 +5651,11 @@ void ActualText::end(const GfxState *state) // extents of all the glyphs inside the span if (actualTextNBytes) { - Unicode *uni = nullptr; - int length; - // now that we have the position info for all of the text inside // the marked content span, we feed the "ActualText" back through // text->addChar() - length = TextStringToUCS4(actualText->toStr(), &uni); - text->addChar(state, actualTextX0, actualTextY0, actualTextX1 - actualTextX0, actualTextY1 - actualTextY0, 0, actualTextNBytes, uni, length); - gfree(uni); + std::vector uni = TextStringToUCS4(actualText->toStr()); + text->addChar(state, actualTextX0, actualTextY0, actualTextX1 - actualTextX0, actualTextY1 - actualTextY0, 0, actualTextNBytes, uni.data(), uni.size()); } delete actualText; @@ -5960,7 +5939,7 @@ bool TextOutputDev::findText(const Unicode *s, int len, bool startAtTop, bool st return text->findText(s, len, startAtTop, stopAtBottom, startAtLast, stopAtLast, caseSensitive, backward, wholeWord, xMin, yMin, xMax, yMax); } -GooString *TextOutputDev::getText(double xMin, double yMin, double xMax, double yMax) const +GooString TextOutputDev::getText(double xMin, double yMin, double xMax, double yMax) const { return text->getText(xMin, yMin, xMax, yMax, textEOL); } @@ -5975,7 +5954,7 @@ std::vector *TextOutputDev::getSelectionRegion(const PDFRectangl return text->getSelectionRegion(selection, style, scale); } -GooString *TextOutputDev::getSelectionText(const PDFRectangle *selection, SelectionStyle style) +GooString TextOutputDev::getSelectionText(const PDFRectangle *selection, SelectionStyle style) { return text->getSelectionText(selection, style); } @@ -6003,6 +5982,8 @@ TextPage *TextOutputDev::takeText() ret = text; text = new TextPage(rawOrder, discardDiag); + delete actualText; + actualText = new ActualText(text); return ret; } diff --git a/poppler/TextOutputDev.h b/poppler/TextOutputDev.h index 5e53bf7..3d4524e 100644 --- a/poppler/TextOutputDev.h +++ b/poppler/TextOutputDev.h @@ -27,6 +27,8 @@ // Copyright (C) 2019, 2022 Oliver Sander // Copyright (C) 2019 Dan Shea // Copyright (C) 2020 Suzuki Toshiya +// Copyright (C) 2024, 2025 Stefan Brüns +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -108,32 +110,14 @@ class POPPLER_PRIVATE_EXPORT TextFontInfo #ifdef TEXTOUT_WORD_LIST // Get the font name (which may be NULL). - const GooString *getFontName() const - { - return fontName; - } + const GooString *getFontName() const { return fontName; } // Get font descriptor flags. - bool isFixedWidth() const - { - return flags & fontFixedWidth; - } - bool isSerif() const - { - return flags & fontSerif; - } - bool isSymbolic() const - { - return flags & fontSymbolic; - } - bool isItalic() const - { - return flags & fontItalic; - } - bool isBold() const - { - return flags & fontBold; - } + bool isFixedWidth() const { return flags & fontFixedWidth; } + bool isSerif() const { return flags & fontSerif; } + bool isSymbolic() const { return flags & fontSymbolic; } + bool isItalic() const { return flags & fontItalic; } + bool isBold() const { return flags & fontBold; } #endif private: @@ -184,30 +168,21 @@ class POPPLER_PRIVATE_EXPORT TextWord // . double primaryDelta(const TextWord *word) const; - static int cmpYX(const void *p1, const void *p2); + static bool cmpYX(const TextWord *const word1, const TextWord *const word2); void visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style); // Get the TextFontInfo object associated with a character. - const TextFontInfo *getFontInfo(int idx) const { return font[idx]; } + const TextFontInfo *getFontInfo(int idx) const { return chars[idx].font; } // Get the next TextWord on the linked list. const TextWord *getNext() const { return next; } #ifdef TEXTOUT_WORD_LIST - int getLength() const - { - return len; - } - const Unicode *getChar(int idx) const - { - return &text[idx]; - } + int getLength() const { return chars.size(); } + const Unicode *getChar(int idx) const { return &chars[idx].text; } GooString *getText() const; - const GooString *getFontName(int idx) const - { - return font[idx]->fontName; - } + const GooString *getFontName(int idx) const { return chars[idx].font->fontName; } void getColor(double *r, double *g, double *b) const { *r = colorR; @@ -222,54 +197,21 @@ class POPPLER_PRIVATE_EXPORT TextWord *yMaxA = yMax; } void getCharBBox(int charIdx, double *xMinA, double *yMinA, double *xMaxA, double *yMaxA) const; - double getFontSize() const - { - return fontSize; - } - int getRotation() const - { - return rot; - } - int getCharPos() const - { - return charPos[0]; - } - int getCharLen() const - { - return charPos[len] - charPos[0]; - } - bool getSpaceAfter() const - { - return spaceAfter; - } + double getFontSize() const { return fontSize; } + int getRotation() const { return rot; } + int getCharPos() const { return chars.empty() ? 0 : chars.front().charPos; } + int getCharLen() const { return chars.empty() ? 0 : chars.back().charPos - chars.front().charPos; } + bool getSpaceAfter() const { return spaceAfter; } #endif - bool isUnderlined() const - { - return underlined; - } - const AnnotLink *getLink() const - { - return link; - } - double getEdge(int i) const - { - return edge[i]; - } - double getBaseline() const - { - return base; - } - bool hasSpaceAfter() const - { - return spaceAfter; - } - const TextWord *nextWord() const - { - return next; - }; + bool isUnderlined() const { return underlined; } + const AnnotLink *getLink() const { return link; } + double getEdge(int i) const { return chars[i].edge; } + double getBaseline() const { return base; } + bool hasSpaceAfter() const { return spaceAfter; } + const TextWord *nextWord() const { return next; }; + auto len() const { return chars.size(); } private: - void ensureCapacity(int capacity); void setInitialBounds(TextFontInfo *fontA, double x, double y); int rot; // rotation, multiple of 90 degrees @@ -278,18 +220,22 @@ class POPPLER_PRIVATE_EXPORT TextWord double xMin, xMax; // bounding box x coordinates double yMin, yMax; // bounding box y coordinates double base; // baseline x or y coordinate - Unicode *text; // the text - CharCode *charcode; // glyph indices - double *edge; // "near" edge x or y coord of each char - // (plus one extra entry for the last char) - int *charPos; // character position (within content stream) - // of each char (plus one extra entry for - // the last char) - int len; // length of text/edge/charPos/font arrays - int size; // size of text/edge/charPos/font arrays - TextFontInfo **font; // font information for each char - Matrix *textMat; // transformation matrix for each char + double fontSize; // font size + + struct CharInfo + { + Unicode text; + CharCode charcode; + int charPos; + double edge; + TextFontInfo *font; + Matrix textMat; + }; + std::vector chars; + int charPosEnd = 0; + double edgeEnd = 0; + bool spaceAfter; // set if there is a space between this // word and the next word on the line bool underlined; @@ -327,20 +273,23 @@ class TextPool TextPool(const TextPool &) = delete; TextPool &operator=(const TextPool &) = delete; - TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; } - void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; } + TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx].head; } + void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx].head = p; } int getBaseIdx(double base) const; void addWord(TextWord *word); + void sort(); private: int minBaseIdx; // min baseline bucket index int maxBaseIdx; // max baseline bucket index - TextWord **pool; // array of linked lists, one for each - // baseline value (multiple of 4 pts) - TextWord *cursor; // pointer to last-accessed word - int cursorBaseIdx; // baseline bucket index of last-accessed word + struct WordList + { + TextWord *head = nullptr; + TextWord *tail = nullptr; + }; + std::vector pool; friend class TextBlock; friend class TextPage; @@ -378,7 +327,7 @@ class TextLine int cmpYX(const TextLine *line) const; - static int cmpXY(const void *p1, const void *p2); + static bool cmpXY(const TextLine *const line1, const TextLine *const line2); void coalesce(const UnicodeMap *uMap); @@ -448,9 +397,7 @@ class TextBlock // Update this block's priMin and priMax values, looking at . void updatePriMinMax(const TextBlock *blk); - static int cmpXYPrimaryRot(const void *p1, const void *p2); - - static int cmpYXPrimaryRot(const void *p1, const void *p2); + static bool cmpXYPrimaryRot(const TextBlock *const blk1, const TextBlock *const blk2); int primaryCmp(const TextBlock *blk) const; @@ -689,7 +636,7 @@ class POPPLER_PRIVATE_EXPORT TextPage double *xMax, double *yMax, PDFRectangle *continueMatch, bool *ignoredHyphen); // Get the text which is inside the specified rectangle. - GooString *getText(double xMin, double yMin, double xMax, double yMax, EndOfLineKind textEOL) const; + GooString getText(double xMin, double yMin, double xMax, double yMax, EndOfLineKind textEOL) const; void visitSelection(TextSelectionVisitor *visitor, const PDFRectangle *selection, SelectionStyle style); @@ -697,9 +644,9 @@ class POPPLER_PRIVATE_EXPORT TextPage std::vector *getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale); - GooString *getSelectionText(const PDFRectangle *selection, SelectionStyle style); + GooString getSelectionText(const PDFRectangle *selection, SelectionStyle style); - std::vector **getSelectionWords(const PDFRectangle *selection, SelectionStyle style, int *nLines); + [[nodiscard]] std::vector>> getSelectionWords(const PDFRectangle *selection, SelectionStyle style); // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns @@ -902,7 +849,7 @@ class POPPLER_PRIVATE_EXPORT TextOutputDev : public OutputDev bool findText(const Unicode *s, int len, bool startAtTop, bool stopAtBottom, bool startAtLast, bool stopAtLast, bool caseSensitive, bool backward, bool wholeWord, double *xMin, double *yMin, double *xMax, double *yMax) const; // Get the text which is inside the specified rectangle. - GooString *getText(double xMin, double yMin, double xMax, double yMax) const; + GooString getText(double xMin, double yMin, double xMax, double yMax) const; // Find a string by character position and length. If found, sets // the text bounding rectangle and returns true; otherwise returns @@ -913,7 +860,7 @@ class POPPLER_PRIVATE_EXPORT TextOutputDev : public OutputDev std::vector *getSelectionRegion(const PDFRectangle *selection, SelectionStyle style, double scale); - GooString *getSelectionText(const PDFRectangle *selection, SelectionStyle style); + GooString getSelectionText(const PDFRectangle *selection, SelectionStyle style); // If true, will combine characters when a base and combining // character are drawn on eachother. @@ -932,10 +879,7 @@ class POPPLER_PRIVATE_EXPORT TextOutputDev : public OutputDev TextPage *takeText(); // Turn extra processing for HTML conversion on or off. - void enableHTMLExtras(bool doHTMLA) - { - doHTML = doHTMLA; - } + void enableHTMLExtras(bool doHTMLA) { doHTML = doHTMLA; } // Get the head of the linked list of TextFlows for the // last rasterized page. @@ -949,22 +893,10 @@ class POPPLER_PRIVATE_EXPORT TextOutputDev : public OutputDev return eolUnix; #endif } - void setTextEOL(EndOfLineKind textEOLA) - { - textEOL = textEOLA; - } - void setTextPageBreaks(bool textPageBreaksA) - { - textPageBreaks = textPageBreaksA; - } - double getMinColSpacing1() const - { - return minColSpacing1; - } - void setMinColSpacing1(double val) - { - minColSpacing1 = val; - } + void setTextEOL(EndOfLineKind textEOLA) { textEOL = textEOLA; } + void setTextPageBreaks(bool textPageBreaksA) { textPageBreaks = textPageBreaksA; } + double getMinColSpacing1() const { return minColSpacing1; } + void setMinColSpacing1(double val) { minColSpacing1 = val; } private: TextOutputFunc outputFunc; // output function diff --git a/poppler/UTF.cc b/poppler/UTF.cc index 9b1bf95..509a48d 100644 --- a/poppler/UTF.cc +++ b/poppler/UTF.cc @@ -14,13 +14,16 @@ // under GPL version 2 or later // // Copyright (C) 2008 Koji Otani -// Copyright (C) 2012, 2017, 2021 Adrian Johnson +// Copyright (C) 2012, 2017, 2021, 2023, 2024 Adrian Johnson // Copyright (C) 2012 Hib Eris -// Copyright (C) 2016, 2018-2022 Albert Astals Cid +// Copyright (C) 2016, 2018-2022, 2024 Albert Astals Cid // Copyright (C) 2016 Jason Crain // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018, 2020 Nelson Benítez León // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2023 Even Rouault +// Copyright (C) 2023, 2024 Oliver Sander // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -37,76 +40,57 @@ #include -bool UnicodeIsValid(Unicode ucs4) +std::vector UTF16toUCS4(std::span utf16) { - return (ucs4 < 0x110000) && ((ucs4 & 0xfffff800) != 0xd800) && (ucs4 < 0xfdd0 || ucs4 > 0xfdef) && ((ucs4 & 0xfffe) != 0xfffe); -} - -int UTF16toUCS4(const Unicode *utf16, int utf16Len, Unicode **ucs4_out) -{ - int i, n, len; - Unicode *u; - // count characters - len = 0; - for (i = 0; i < utf16Len; i++) { - if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { + int len = 0; + for (size_t i = 0; i < utf16.size(); i++) { + if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00 && i + 1 < utf16.size() && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { i++; /* surrogate pair */ } len++; } - if (ucs4_out == nullptr) { - return len; - } - - u = (Unicode *)gmallocn(len, sizeof(Unicode)); - n = 0; + std::vector u; + u.reserve(len); // convert string - for (i = 0; i < utf16Len; i++) { + for (size_t i = 0; i < utf16.size(); i++) { if (utf16[i] >= 0xd800 && utf16[i] < 0xdc00) { /* surrogate pair */ - if (i + 1 < utf16Len && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { + if (i + 1 < utf16.size() && utf16[i + 1] >= 0xdc00 && utf16[i + 1] < 0xe000) { /* next code is a low surrogate */ - u[n] = (((utf16[i] & 0x3ff) << 10) | (utf16[i + 1] & 0x3ff)) + 0x10000; + u.push_back((((utf16[i] & 0x3ff) << 10) | (utf16[i + 1] & 0x3ff)) + 0x10000); ++i; } else { /* missing low surrogate replace it with REPLACEMENT CHARACTER (U+FFFD) */ - u[n] = 0xfffd; + u.push_back(0xfffd); } } else if (utf16[i] >= 0xdc00 && utf16[i] < 0xe000) { /* invalid low surrogate replace it with REPLACEMENT CHARACTER (U+FFFD) */ - u[n] = 0xfffd; + u.push_back(0xfffd); } else { - u[n] = utf16[i]; + u.push_back(utf16[i]); } - if (!UnicodeIsValid(u[n])) { - u[n] = 0xfffd; + if (!UnicodeIsValid(u.back())) { + u.back() = 0xfffd; } - n++; } - *ucs4_out = u; - return len; + return u; } -int TextStringToUCS4(const std::string &textStr, Unicode **ucs4) +std::vector TextStringToUCS4(std::string_view textStr) { - int i, len; - const char *s; - Unicode *u; bool isUnicode, isUnicodeLE; - len = textStr.size(); - s = textStr.c_str(); + int len = textStr.size(); if (len == 0) { - *ucs4 = nullptr; - return 0; + return {}; } - if (GooString::hasUnicodeMarker(textStr)) { + if (hasUnicodeByteOrderMark(textStr)) { isUnicode = true; isUnicodeLE = false; - } else if (GooString::hasUnicodeMarkerLE(textStr)) { + } else if (hasUnicodeByteOrderMarkLE(textStr)) { isUnicode = false; isUnicodeLE = true; } else { @@ -115,30 +99,30 @@ int TextStringToUCS4(const std::string &textStr, Unicode **ucs4) } if (isUnicode || isUnicodeLE) { - Unicode *utf16; len = len / 2 - 1; if (len > 0) { - utf16 = new Unicode[len]; - for (i = 0; i < len; i++) { + std::vector utf16; + utf16.reserve(len); + for (int i = 0; i < len; i++) { if (isUnicode) { - utf16[i] = (s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff); + utf16.push_back((textStr[2 + i * 2] & 0xff) << 8 | (textStr[3 + i * 2] & 0xff)); } else { // UnicodeLE - utf16[i] = (s[3 + i * 2] & 0xff) << 8 | (s[2 + i * 2] & 0xff); + utf16.push_back((textStr[3 + i * 2] & 0xff) << 8 | (textStr[2 + i * 2] & 0xff)); } } - len = UTF16toUCS4(utf16, len, &u); - delete[] utf16; + return UTF16toUCS4(utf16); + } else { - u = nullptr; + return {}; } } else { - u = (Unicode *)gmallocn(len, sizeof(Unicode)); - for (i = 0; i < len; i++) { - u[i] = pdfDocEncoding[s[i] & 0xff]; + std::vector u; + u.reserve(len); + for (int i = 0; i < len; i++) { + u.push_back(pdfDocEncoding[textStr[i] & 0xff]); } + return u; } - *ucs4 = u; - return len; } bool UnicodeIsWhitespace(Unicode ucs4) @@ -222,67 +206,42 @@ inline uint32_t decodeUtf8(uint32_t *state, uint32_t *codep, char byte) return *state; } -int utf8CountUCS4(const char *utf8) +std::vector utf8ToUCS4(std::string_view utf8) { uint32_t codepoint; uint32_t state = 0; - int count = 0; - - while (*utf8) { - decodeUtf8(&state, &codepoint, *utf8); - if (state == UTF8_ACCEPT) { - count++; - } else if (state == UTF8_REJECT) { - count++; // replace with REPLACEMENT_CHAR - state = 0; - } - utf8++; - } - if (state != UTF8_ACCEPT && state != UTF8_REJECT) { - count++; // replace with REPLACEMENT_CHAR - } - return count; -} + std::vector u; -int utf8ToUCS4(const char *utf8, Unicode **ucs4_out) -{ - int len = utf8CountUCS4(utf8); - Unicode *u = (Unicode *)gmallocn(len, sizeof(Unicode)); - int n = 0; - uint32_t codepoint; - uint32_t state = 0; - - while (*utf8 && n < len) { - decodeUtf8(&state, &codepoint, *utf8); + for (auto c : utf8) { + decodeUtf8(&state, &codepoint, c); if (state == UTF8_ACCEPT) { - u[n++] = codepoint; + u.push_back(codepoint); } else if (state == UTF8_REJECT) { - u[n++] = REPLACEMENT_CHAR; // invalid byte for this position + u.push_back(REPLACEMENT_CHAR); // invalid byte for this position state = 0; } - utf8++; } if (state != UTF8_ACCEPT && state != UTF8_REJECT) { - u[n] = REPLACEMENT_CHAR; // invalid byte for this position + u.push_back(REPLACEMENT_CHAR); // invalid byte for this position } + u.shrink_to_fit(); - *ucs4_out = u; - return len; + return u; } // Count number of UTF-16 code units required to convert a UTF-8 string // (excluding terminating NULL). Each invalid byte is counted as a // code point since the UTF-8 conversion functions will replace it with // REPLACEMENT_CHAR. -int utf8CountUtf16CodeUnits(const char *utf8) +int utf8CountUtf16CodeUnits(std::string_view utf8) { uint32_t codepoint; uint32_t state = 0; int count = 0; - while (*utf8) { - decodeUtf8(&state, &codepoint, *utf8); + for (auto c : utf8) { + decodeUtf8(&state, &codepoint, c); if (state == UTF8_ACCEPT) { if (codepoint < 0x10000) { count++; @@ -295,7 +254,6 @@ int utf8CountUtf16CodeUnits(const char *utf8) count++; // replace with REPLACEMENT_CHAR state = 0; } - utf8++; } if (state != UTF8_ACCEPT && state != UTF8_REJECT) { count++; // replace with REPLACEMENT_CHAR @@ -304,84 +262,52 @@ int utf8CountUtf16CodeUnits(const char *utf8) return count; } -// Convert UTF-8 to UTF-16 -// utf8- UTF-8 string to convert. If not null terminated, set maxUtf8 to num -// bytes to convert -// utf16 - output buffer to write UTF-16 to. Output will always be null terminated. -// maxUtf16 - maximum size of output buffer including space for null. -// maxUtf8 - maximum number of UTF-8 bytes to convert. Conversion stops when -// either this count is reached or a null is encountered. -// Returns number of UTF-16 code units written (excluding NULL). -int utf8ToUtf16(const char *utf8, uint16_t *utf16, int maxUtf16, int maxUtf8) +std::u16string utf8ToUtf16(std::string_view utf8) { - uint16_t *p = utf16; uint32_t codepoint; uint32_t state = 0; - int nIn = 0; - int nOut = 0; - while (*utf8 && nIn < maxUtf8 && nOut < maxUtf16 - 1) { - decodeUtf8(&state, &codepoint, *utf8); + if (isUtf8WithBom(utf8)) { + utf8 = utf8.substr(3); + } + std::u16string utf16; + for (auto c : utf8) { + decodeUtf8(&state, &codepoint, c); if (state == UTF8_ACCEPT) { if (codepoint < 0x10000) { - *p++ = (uint16_t)codepoint; - nOut++; + utf16.push_back((uint16_t)codepoint); } else if (codepoint <= UCS4_MAX) { - *p++ = (uint16_t)(0xD7C0 + (codepoint >> 10)); - *p++ = (uint16_t)(0xDC00 + (codepoint & 0x3FF)); - nOut += 2; + utf16.push_back((uint16_t)(0xD7C0 + (codepoint >> 10))); + utf16.push_back((uint16_t)(0xDC00 + (codepoint & 0x3FF))); } else { - *p++ = REPLACEMENT_CHAR; - nOut++; + utf16.push_back(REPLACEMENT_CHAR); state = 0; } } else if (state == UTF8_REJECT) { - *p++ = REPLACEMENT_CHAR; // invalid byte for this position - nOut++; + utf16.push_back(REPLACEMENT_CHAR); // invalid byte for this position } - utf8++; - nIn++; } // replace any trailing bytes too short for a valid UTF-8 with a replacement char - if (state != UTF8_ACCEPT && state != UTF8_REJECT && nOut < maxUtf16 - 1) { - *p++ = REPLACEMENT_CHAR; - nOut++; - } - if (nOut > maxUtf16 - 1) { - nOut = maxUtf16 - 1; - } - utf16[nOut] = 0; - return nOut; -} - -// Allocate utf16 string and convert utf8 into it. -uint16_t *utf8ToUtf16(const char *utf8, int *len) -{ - int n = utf8CountUtf16CodeUnits(utf8); - if (len) { - *len = n; + if (state != UTF8_ACCEPT && state != UTF8_REJECT) { + utf16.push_back(REPLACEMENT_CHAR); } - uint16_t *utf16 = (uint16_t *)gmallocn(n + 1, sizeof(uint16_t)); - utf8ToUtf16(utf8, utf16); return utf16; } -std::unique_ptr utf8ToUtf16WithBom(const std::string &utf8) +std::string utf8ToUtf16WithBom(std::string_view utf8) { - auto result = std::make_unique(); if (utf8.empty()) { - return result; + return {}; } - int tmp_length; // Number of UTF-16 symbols. - char *tmp_str = (char *)utf8ToUtf16(utf8.c_str(), &tmp_length); + std::u16string utf16 = utf8ToUtf16(utf8); + char *tmp_str = (char *)utf16.data(); #ifndef WORDS_BIGENDIAN - for (int i = 0; i < tmp_length; i++) { + for (size_t i = 0; i < utf16.size(); i++) { std::swap(tmp_str[i * 2], tmp_str[i * 2 + 1]); } #endif - result->prependUnicodeMarker(); - result->append(tmp_str, tmp_length * 2); - gfree(tmp_str); + std::string result(unicodeByteOrderMark); + result.append(tmp_str, utf16.size() * 2); return result; } @@ -445,28 +371,20 @@ int utf16CountUtf8Bytes(const uint16_t *utf16) utf16++; } if (state != UTF8_ACCEPT && state != UTF8_REJECT) { - count++; // replace with REPLACEMENT_CHAR + count += 3; // replace with REPLACEMENT_CHAR } return count; } -// Convert UTF-16 to UTF-8 -// utf16- UTF-16 string to convert. If not null terminated, set maxUtf16 to num -// code units to convert -// utf8 - output buffer to write UTF-8 to. Output will always be null terminated. -// maxUtf8 - maximum size of output buffer including space for null. -// maxUtf16 - maximum number of UTF-16 code units to convert. Conversion stops when -// either this count is reached or a null is encountered. -// Returns number of UTF-8 bytes written (excluding NULL). -int utf16ToUtf8(const uint16_t *utf16, char *utf8, int maxUtf8, int maxUtf16) +std::string utf16ToUtf8(const uint16_t *utf16, int maxUtf16) { uint32_t codepoint = 0; uint32_t state = 0; int nIn = 0; - int nOut = 0; - char *p = utf8; - while (*utf16 && nIn < maxUtf16 && nOut < maxUtf8 - 1) { + char p[4]; + std::string utf8; + while (*utf16 && nIn < maxUtf16) { decodeUtf16(&state, &codepoint, *utf16); if (state == UTF16_ACCEPT || state == UTF16_REJECT) { if (state == UTF16_REJECT || codepoint > UCS4_MAX) { @@ -474,47 +392,26 @@ int utf16ToUtf8(const uint16_t *utf16, char *utf8, int maxUtf8, int maxUtf16) state = 0; } - int bufSize = maxUtf8 - nOut; - int count = mapUTF8(codepoint, p, bufSize); - p += count; - nOut += count; + int count = mapUTF8(codepoint, p, 4); + utf8.append(std::string_view(p, count)); } utf16++; nIn++; } // replace any trailing bytes too short for a valid UTF-8 with a replacement char - if (state != UTF16_ACCEPT && state != UTF16_REJECT && nOut < maxUtf8 - 1) { - int bufSize = maxUtf8 - nOut; - int count = mapUTF8(REPLACEMENT_CHAR, p, bufSize); - p += count; - nOut += count; - nOut++; - } - if (nOut > maxUtf8 - 1) { - nOut = maxUtf8 - 1; + if (state != UTF16_ACCEPT && state != UTF16_REJECT) { + int count = mapUTF8(REPLACEMENT_CHAR, p, 4); + utf8.append(std::string_view(p, count)); } - utf8[nOut] = 0; - return nOut; -} - -// Allocate utf8 string and convert utf16 into it. -char *utf16ToUtf8(const uint16_t *utf16, int *len) -{ - int n = utf16CountUtf8Bytes(utf16); - if (len) { - *len = n; - } - char *utf8 = (char *)gmalloc(n + 1); - utf16ToUtf8(utf16, utf8); return utf8; } -void unicodeToAscii7(const Unicode *in, int len, Unicode **ucs4_out, int *out_len, const int *in_idx, int **indices) +void unicodeToAscii7(std::span in, Unicode **ucs4_out, int *out_len, const int *in_idx, int **indices) { const UnicodeMap *uMap = globalParams->getUnicodeMap("ASCII7"); int *idx = nullptr; - if (!len) { + if (in.empty()) { *ucs4_out = nullptr; *out_len = 0; return; @@ -524,16 +421,17 @@ void unicodeToAscii7(const Unicode *in, int len, Unicode **ucs4_out, int *out_le if (!in_idx) { indices = nullptr; } else { - idx = (int *)gmallocn(len * 8 + 1, sizeof(int)); + idx = (int *)gmallocn(in.size() * 8 + 1, sizeof(int)); } } std::string str; char buf[8]; // 8 is enough for mapping an unicode char to a string - int i, n, k; + size_t i; + int n, k; - for (i = k = 0; i < len; ++i) { + for (i = k = 0; i < in.size(); ++i) { n = uMap->mapUnicode(in[i], buf, sizeof(buf)); if (!n) { // the Unicode char could not be converted to ascii7 counterpart @@ -549,10 +447,43 @@ void unicodeToAscii7(const Unicode *in, int len, Unicode **ucs4_out, int *out_le } } - *out_len = TextStringToUCS4(str, ucs4_out); + std::vector ucs4 = TextStringToUCS4(str); + *out_len = ucs4.size(); + *ucs4_out = (Unicode *)gmallocn(ucs4.size(), sizeof(Unicode)); + memcpy(*ucs4_out, ucs4.data(), ucs4.size() * sizeof(Unicode)); if (indices) { - idx[k] = in_idx[len]; + idx[k] = in_idx[in.size()]; *indices = idx; } } + +// Convert a PDF Text String to UTF-8 +// textStr - PDF text string +// returns UTF-8 string. +std::string TextStringToUtf8(const std::string &textStr) +{ + int i, len; + const char *s; + std::string utf8; + + len = textStr.size(); + s = textStr.c_str(); + if (hasUnicodeByteOrderMark(textStr)) { + std::vector utf16; + len = len / 2 - 1; + utf16.resize(len + 1); + for (i = 0; i < len; i++) { + utf16[i] = (s[2 + i * 2] & 0xff) << 8 | (s[3 + i * 2] & 0xff); + } + utf16[i] = 0; + utf8 = utf16ToUtf8(utf16.data(), utf16.size()); + } else { + utf8.resize(len + 1); + for (i = 0; i < len; i++) { + utf8[i] = pdfDocEncoding[s[i] & 0xff]; + } + utf8[i] = 0; + } + return utf8; +} diff --git a/poppler/UTF.h b/poppler/UTF.h index 626c686..4a9ee14 100644 --- a/poppler/UTF.h +++ b/poppler/UTF.h @@ -4,12 +4,15 @@ // // This file is licensed under the GPLv2 or later // -// Copyright (C) 2012, 2017, 2021 Adrian Johnson +// Copyright (C) 2012, 2017, 2021, 2023, 2024 Adrian Johnson // Copyright (C) 2016 Jason Crain // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Nelson Benítez León -// Copyright (C) 2019-2022 Albert Astals Cid +// Copyright (C) 2019-2022, 2024 Albert Astals Cid // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2023 Even Rouault +// Copyright (C) 2023, 2024 Oliver Sander // //======================================================================== @@ -18,83 +21,110 @@ #include #include -#include +#include +#include +#include -#include "goo/GooString.h" #include "CharTypes.h" #include "poppler_private_export.h" +// Magic bytes that mark the byte order in a UTF-16 unicode string (big-endian case) +constexpr std::string_view unicodeByteOrderMark = "\xFE\xFF"; + +// Magic bytes that mark the byte order in a UTF-16 unicode string (little-endian case) +constexpr std::string_view unicodeByteOrderMarkLE = "\xFF\xFE"; + // Convert a UTF-16 string to a UCS-4 // utf16 - utf16 bytes // utf16_len - number of UTF-16 characters -// ucs4_out - if not NULL, allocates and returns UCS-4 string. Free with gfree. // returns number of UCS-4 characters -int UTF16toUCS4(const Unicode *utf16, int utf16Len, Unicode **ucs4_out); +std::vector UTF16toUCS4(std::span utf16); // Convert a PDF Text String to UCS-4 // s - PDF text string -// ucs4 - if the number of UCS-4 characters is > 0, allocates and -// returns UCS-4 string. Free with gfree. -// returns number of UCS-4 characters -int POPPLER_PRIVATE_EXPORT TextStringToUCS4(const std::string &textStr, Unicode **ucs4); +// returns UCS-4 characters +// Convert a PDF text string to UCS-4 +std::vector POPPLER_PRIVATE_EXPORT TextStringToUCS4(std::string_view textStr); // check if UCS-4 character is valid -bool UnicodeIsValid(Unicode ucs4); +inline bool UnicodeIsValid(Unicode ucs4) +{ + return (ucs4 < 0x110000) && ((ucs4 & 0xfffff800) != 0xd800) && (ucs4 < 0xfdd0 || ucs4 > 0xfdef) && ((ucs4 & 0xfffe) != 0xfffe); +} + +// check whether string starts with Big-Endian byte order mark +inline bool hasUnicodeByteOrderMark(std::string_view s) +{ + return s.starts_with(unicodeByteOrderMark); +} + +// check whether string starts with Little-Endian byte order mark +inline bool hasUnicodeByteOrderMarkLE(std::string_view s) +{ + return s.starts_with(unicodeByteOrderMarkLE); +} + +// put big-endian unicode byte order mark at the beginning of a string +inline void prependUnicodeByteOrderMark(std::string &s) +{ + s.insert(0, unicodeByteOrderMark); +} + +// check whether string starts with Big-Endian byte order mark and string length is even +inline bool hasUnicodeByteOrderMarkAndLengthIsEven(std::string_view s) +{ + return s.starts_with(unicodeByteOrderMark) && s.length() % 2 == 0; +} // is a unicode whitespace character bool UnicodeIsWhitespace(Unicode ucs4); -// Count number of UCS-4 characters required to convert a UTF-8 string to -// UCS-4 (excluding terminating NULL). -int POPPLER_PRIVATE_EXPORT utf8CountUCS4(const char *utf8); - // Convert a UTF-8 string to a UCS-4 // utf8 - utf8 bytes // ucs4_out - if not NULL, allocates and returns UCS-4 string. Free with gfree. // returns number of UCS-4 characters -int POPPLER_PRIVATE_EXPORT utf8ToUCS4(const char *utf8, Unicode **ucs4_out); +std::vector POPPLER_PRIVATE_EXPORT utf8ToUCS4(std::string_view utf8); // Count number of UTF-16 code units required to convert a UTF-8 string // (excluding terminating NULL). Each invalid byte is counted as a // code point since the UTF-8 conversion functions will replace it with // REPLACEMENT_CHAR. -int POPPLER_PRIVATE_EXPORT utf8CountUtf16CodeUnits(const char *utf8); +int POPPLER_PRIVATE_EXPORT utf8CountUtf16CodeUnits(std::string_view utf8); // Convert UTF-8 to UTF-16 -// utf8- UTF-8 string to convert. If not null terminated, set maxUtf8 to num -// bytes to convert -// utf16 - output buffer to write UTF-16 to. Output will always be null terminated. -// maxUtf16 - maximum size of output buffer including space for null. -// maxUtf8 - maximum number of UTF-8 bytes to convert. Conversion stops when -// either this count is reached or a null is encountered. -// Returns number of UTF-16 code units written (excluding NULL). -int POPPLER_PRIVATE_EXPORT utf8ToUtf16(const char *utf8, uint16_t *utf16, int maxUtf16 = INT_MAX, int maxUtf8 = INT_MAX); - -// Allocate utf16 string and convert utf8 into it. -uint16_t POPPLER_PRIVATE_EXPORT *utf8ToUtf16(const char *utf8, int *len = nullptr); +// utf8 - UTF-8 string to convert. If not null terminated, ensure +// maxUtf8 is set the the exact number of bytes to convert. +// Returns utf16 string +std::u16string POPPLER_PRIVATE_EXPORT utf8ToUtf16(std::string_view utf8); + +inline bool isUtf8WithBom(std::string_view str) +{ + if (str.size() < 4) { + return false; + } + if (str[0] == '\xef' && str[1] == '\xbb' && str[2] == '\xbf') { + return true; + } + return false; +} // Converts a UTF-8 string to a big endian UTF-16 string with BOM. // The caller owns the returned pointer. // utf8 - UTF-8 string to convert. An empty string is acceptable. // Returns a big endian UTF-16 string with BOM or an empty string without BOM. -std::unique_ptr POPPLER_PRIVATE_EXPORT utf8ToUtf16WithBom(const std::string &utf8); +std::string POPPLER_PRIVATE_EXPORT utf8ToUtf16WithBom(std::string_view utf8); // Count number of UTF-8 bytes required to convert a UTF-16 string to // UTF-8 (excluding terminating NULL). int POPPLER_PRIVATE_EXPORT utf16CountUtf8Bytes(const uint16_t *utf16); // Convert UTF-16 to UTF-8 -// utf16- UTF-16 string to convert. If not null terminated, set maxUtf16 to num -// code units to convert -// utf8 - output buffer to write UTF-8 to. Output will always be null terminated. -// maxUtf8 - maximum size of output buffer including space for null. -// maxUtf16 - maximum number of UTF-16 code units to convert. Conversion stops when -// either this count is reached or a null is encountered. -// Returns number of UTF-8 bytes written (excluding NULL). -int POPPLER_PRIVATE_EXPORT utf16ToUtf8(const uint16_t *utf16, char *utf8, int maxUtf8 = INT_MAX, int maxUtf16 = INT_MAX); - -// Allocate utf8 string and convert utf16 into it. -char POPPLER_PRIVATE_EXPORT *utf16ToUtf8(const uint16_t *utf16, int *len = nullptr); +// utf16 - UTF-16 string to convert. If not null terminated, ensure +// maxUtf16 is set the the exact number of code units to convert. +// maxUtf16 - Maximum number of UTF-16 code units to convert. Conversion stops +// when either this count is reached or a null is encountered. +// Returns utf8 string. +std::string POPPLER_PRIVATE_EXPORT utf16ToUtf8(const uint16_t *utf16, int maxUtf16 = INT_MAX); // Convert a UCS-4 string to pure ASCII (7bit) // in - UCS-4 string bytes @@ -107,6 +137,11 @@ char POPPLER_PRIVATE_EXPORT *utf16ToUtf8(const uint16_t *utf16, int *len = nullp // of length @out_len + 1, for each character in the ascii string giving the index // of the corresponding character in the text of the line (thanks to this info // being passed in @in_idx parameter). -void POPPLER_PRIVATE_EXPORT unicodeToAscii7(const Unicode *in, int len, Unicode **ucs4_out, int *out_len, const int *in_idx, int **indices); +void POPPLER_PRIVATE_EXPORT unicodeToAscii7(std::span in, Unicode **ucs4_out, int *out_len, const int *in_idx, int **indices); + +// Convert a PDF Text String to UTF-8 +// textStr - PDF text string +// returns UTF-8 string. +std::string POPPLER_PRIVATE_EXPORT TextStringToUtf8(const std::string &textStr); #endif diff --git a/poppler/UnicodeCClassTables.h b/poppler/UnicodeCClassTables.h index 88d119b..4a90f27 100644 --- a/poppler/UnicodeCClassTables.h +++ b/poppler/UnicodeCClassTables.h @@ -5,6 +5,8 @@ // Can be regenerated from gen-unicode-tables.pl, also in GLib, with relevant // Unicode data tables from ftp.unicode.org. +#include "UnicodeTypeTable.h" + static const Unicode cclass_data[][256] = { { /* page 3, index 0 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 220, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, diff --git a/poppler/UnicodeCompTables.h b/poppler/UnicodeCompTables.h index f74ffcd..77d8818 100644 --- a/poppler/UnicodeCompTables.h +++ b/poppler/UnicodeCompTables.h @@ -5,6 +5,8 @@ // Can be regenerated from gen-unicode-tables.pl, also in GLib, with relevant // Unicode data tables from ftp.unicode.org. +#include "UnicodeTypeTable.h" + #define COMPOSE_FIRST_START 1 #define COMPOSE_FIRST_SINGLE_START 147 #define COMPOSE_SECOND_START 357 diff --git a/poppler/UnicodeDecompTables.h b/poppler/UnicodeDecompTables.h index 8cb793c..fb245e3 100644 --- a/poppler/UnicodeDecompTables.h +++ b/poppler/UnicodeDecompTables.h @@ -1,5 +1,7 @@ // Generated by gen-unicode-tables.py +#include "CharTypes.h" + typedef struct { Unicode character; diff --git a/poppler/UnicodeMap.cc b/poppler/UnicodeMap.cc index a450df9..78c05d6 100644 --- a/poppler/UnicodeMap.cc +++ b/poppler/UnicodeMap.cc @@ -20,6 +20,7 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Oliver Sander // Copyright (C) 2019 Volker Krause +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -38,27 +39,18 @@ #include "GlobalParams.h" #include "UnicodeMap.h" -//------------------------------------------------------------------------ - -#define maxExtCode 16 - -struct UnicodeMapExt -{ - Unicode u; // Unicode char - char code[maxExtCode]; - unsigned int nBytes; -}; +// helper for using std::visit to get a dependent false for static_asserts +// to help get compile errors if one ever extends variants +template +inline constexpr bool always_false_v = false; //------------------------------------------------------------------------ std::unique_ptr UnicodeMap::parse(const std::string &encodingNameA) { FILE *f; - UnicodeMapRange *range; - UnicodeMapExt *eMap; - int size, eMapsSize; char buf[256]; - int line, nBytes, i; + int line, nBytes; char *tok1, *tok2, *tok3; char *tokptr; @@ -69,9 +61,8 @@ std::unique_ptr UnicodeMap::parse(const std::string &encodingNameA) auto map = std::unique_ptr(new UnicodeMap(encodingNameA)); - size = 8; - UnicodeMapRange *customRanges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange)); - eMapsSize = 0; + std::vector customRanges; + std::vector eMap; line = 1; while (getLine(buf, sizeof(buf), f)) { @@ -82,30 +73,22 @@ std::unique_ptr UnicodeMap::parse(const std::string &encodingNameA) } nBytes = strlen(tok3) / 2; if (nBytes <= 4) { - if (map->len == size) { - size *= 2; - customRanges = (UnicodeMapRange *)greallocn(customRanges, size, sizeof(UnicodeMapRange)); - } - range = &customRanges[map->len]; - sscanf(tok1, "%x", &range->start); - sscanf(tok2, "%x", &range->end); - sscanf(tok3, "%x", &range->code); - range->nBytes = nBytes; - ++map->len; + UnicodeMapRange range; + sscanf(tok1, "%x", &range.start); + sscanf(tok2, "%x", &range.end); + sscanf(tok3, "%x", &range.code); + range.nBytes = nBytes; + customRanges.push_back(range); } else if (tok2 == tok1) { - if (map->eMapsLen == eMapsSize) { - eMapsSize += 16; - map->eMaps = (UnicodeMapExt *)greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt)); - } - eMap = &map->eMaps[map->eMapsLen]; - sscanf(tok1, "%x", &eMap->u); - for (i = 0; i < nBytes; ++i) { + UnicodeMapExt ext; + sscanf(tok1, "%x", &ext.u); + ext.code.reserve(nBytes); + for (int i = 0; i < nBytes; ++i) { unsigned int x; sscanf(tok3 + i * 2, "%2x", &x); - eMap->code[i] = (char)x; + ext.code.push_back((char)x); } - eMap->nBytes = nBytes; - ++map->eMapsLen; + eMap.push_back(std::move(ext)); } else { error(errSyntaxError, -1, "Bad line ({0:d}) in unicodeMap file for the '{1:s}' encoding", line, encodingNameA.c_str()); } @@ -117,7 +100,8 @@ std::unique_ptr UnicodeMap::parse(const std::string &encodingNameA) fclose(f); - map->ranges = customRanges; + map->eMaps = std::move(eMap); + map->data = std::move(customRanges); return map; } @@ -125,58 +109,25 @@ UnicodeMap::UnicodeMap(const std::string &encodingNameA) { encodingName = encodingNameA; unicodeOut = false; - kind = unicodeMapUser; - ranges = nullptr; - len = 0; - eMaps = nullptr; - eMapsLen = 0; } -UnicodeMap::UnicodeMap(const char *encodingNameA, bool unicodeOutA, const UnicodeMapRange *rangesA, int lenA) +UnicodeMap::UnicodeMap(const char *encodingNameA, bool unicodeOutA, std::span rangesA) { encodingName = encodingNameA; unicodeOut = unicodeOutA; - kind = unicodeMapResident; - ranges = rangesA; - len = lenA; - eMaps = nullptr; - eMapsLen = 0; + data = rangesA; } UnicodeMap::UnicodeMap(const char *encodingNameA, bool unicodeOutA, UnicodeMapFunc funcA) { encodingName = encodingNameA; unicodeOut = unicodeOutA; - kind = unicodeMapFunc; - func = funcA; - eMaps = nullptr; - eMapsLen = 0; + data = funcA; } -UnicodeMap::~UnicodeMap() -{ - if (kind == unicodeMapUser && ranges) { - gfree(const_cast(ranges)); - } - if (eMaps) { - gfree(eMaps); - } -} +UnicodeMap::~UnicodeMap() = default; -UnicodeMap::UnicodeMap(UnicodeMap &&other) noexcept : encodingName { std::move(other.encodingName) }, kind { other.kind }, unicodeOut { other.unicodeOut }, len { other.len }, eMaps { other.eMaps }, eMapsLen { other.eMapsLen } -{ - switch (kind) { - case unicodeMapUser: - case unicodeMapResident: - ranges = other.ranges; - other.ranges = nullptr; - break; - case unicodeMapFunc: - func = other.func; - break; - } - other.eMaps = nullptr; -} +UnicodeMap::UnicodeMap(UnicodeMap &&other) noexcept : encodingName { std::move(other.encodingName) }, unicodeOut { other.unicodeOut }, data { std::move(other.data) }, eMaps { std::move(other.eMaps) } { } UnicodeMap &UnicodeMap::operator=(UnicodeMap &&other) noexcept { @@ -191,41 +142,8 @@ void UnicodeMap::swap(UnicodeMap &other) noexcept using std::swap; swap(encodingName, other.encodingName); swap(unicodeOut, other.unicodeOut); - switch (kind) { - case unicodeMapUser: - case unicodeMapResident: - switch (other.kind) { - case unicodeMapUser: - case unicodeMapResident: - swap(ranges, other.ranges); - break; - case unicodeMapFunc: { - const auto tmp = ranges; - func = other.func; - other.ranges = tmp; - break; - } - } - break; - case unicodeMapFunc: - switch (other.kind) { - case unicodeMapUser: - case unicodeMapResident: { - const auto tmp = func; - ranges = other.ranges; - other.func = tmp; - break; - } - case unicodeMapFunc: - swap(func, other.func); - break; - } - break; - } - swap(kind, other.kind); - swap(len, other.len); + swap(data, other.data); swap(eMaps, other.eMaps); - swap(eMapsLen, other.eMapsLen); } bool UnicodeMap::match(const std::string &encodingNameA) const @@ -235,55 +153,60 @@ bool UnicodeMap::match(const std::string &encodingNameA) const int UnicodeMap::mapUnicode(Unicode u, char *buf, int bufSize) const { - int a, b, m, n, i, j; - unsigned int code; - - if (kind == unicodeMapFunc) { - return (*func)(u, buf, bufSize); - } - - a = 0; - b = len; - if (u >= ranges[a].start) { - // invariant: ranges[a].start <= u < ranges[b].start - while (b - a > 1) { - m = (a + b) / 2; - if (u >= ranges[m].start) { - a = m; - } else if (u < ranges[m].start) { - b = m; - } - } - if (u <= ranges[a].end) { - n = ranges[a].nBytes; - if (n > bufSize) { - return 0; - } - code = ranges[a].code + (u - ranges[a].start); - for (i = n - 1; i >= 0; --i) { - buf[i] = (char)(code & 0xff); - code >>= 8; - } - return n; - } - } - - for (i = 0; i < eMapsLen; ++i) { - if (eMaps[i].u == u) { - n = eMaps[i].nBytes; - for (j = 0; j < n; ++j) { - buf[j] = eMaps[i].code[j]; - } - return n; - } - } - return 0; + return std::visit( + [this, u, buf, bufSize](auto &&item) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return (*item)(u, buf, bufSize); + } else if constexpr (std::is_same_v> || std::is_same_v>) { + int a = 0; + int b = (int)item.size(); + if (u >= item[a].start) { + // invariant: item[a].start <= u < item[b].start + while (b - a > 1) { + int m = (a + b) / 2; + if (u >= item[m].start) { + a = m; + } else if (u < item[m].start) { + b = m; + } + } + if (u <= item[a].end) { + int n = item[a].nBytes; + if (n > bufSize) { + return 0; + } + unsigned int code = item[a].code + (u - item[a].start); + for (int i = n - 1; i >= 0; --i) { + buf[i] = (char)(code & 0xff); + code >>= 8; + } + return n; + } + } + for (const UnicodeMapExt &ext : eMaps) { + if (ext.u == u) { + if (int(ext.code.size()) >= bufSize) { + return 0; + } + for (int j = 0; j < std::min(int(ext.code.size()), bufSize); ++j) { + buf[j] = ext.code[j]; + } + return int(ext.code.size()); + } + } + return 0; + } else { + static_assert(always_false_v); + } + }, + data); } //------------------------------------------------------------------------ -UnicodeMapCache::UnicodeMapCache() { } +UnicodeMapCache::UnicodeMapCache() = default; const UnicodeMap *UnicodeMapCache::getUnicodeMap(const std::string &encodingName) { diff --git a/poppler/UnicodeMap.h b/poppler/UnicodeMap.h index 0d80c02..773a431 100644 --- a/poppler/UnicodeMap.h +++ b/poppler/UnicodeMap.h @@ -19,6 +19,7 @@ // Copyright (C) 2018-2022 Albert Astals Cid // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Volker Krause +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -36,16 +37,11 @@ #include #include #include +#include +#include //------------------------------------------------------------------------ -enum UnicodeMapKind -{ - unicodeMapUser, // read from a file - unicodeMapResident, // static list of ranges - unicodeMapFunc // function pointer -}; - typedef int (*UnicodeMapFunc)(Unicode u, char *buf, int bufSize); struct UnicodeMapRange @@ -54,7 +50,11 @@ struct UnicodeMapRange unsigned int code, nBytes; // first output code }; -struct UnicodeMapExt; +struct UnicodeMapExt +{ + Unicode u; // Unicode char + std::vector code; +}; //------------------------------------------------------------------------ @@ -66,7 +66,7 @@ class POPPLER_PRIVATE_EXPORT UnicodeMap static std::unique_ptr parse(const std::string &encodingNameA); // Create a resident UnicodeMap. - UnicodeMap(const char *encodingNameA, bool unicodeOutA, const UnicodeMapRange *rangesA, int lenA); + UnicodeMap(const char *encodingNameA, bool unicodeOutA, std::span rangesA); // Create a resident UnicodeMap that uses a function instead of a // list of ranges. @@ -100,15 +100,9 @@ class POPPLER_PRIVATE_EXPORT UnicodeMap explicit UnicodeMap(const std::string &encodingNameA); std::string encodingName; - UnicodeMapKind kind; bool unicodeOut; - union { - const UnicodeMapRange *ranges; // (user, resident) - UnicodeMapFunc func; // (func) - }; - int len; // (user, resident) - UnicodeMapExt *eMaps; // (user) - int eMapsLen; // (user) + std::variant, std::span, UnicodeMapFunc> data; + std::vector eMaps; // (user) }; //------------------------------------------------------------------------ diff --git a/poppler/UnicodeMapTables.h b/poppler/UnicodeMapTables.h index 7c89feb..bfb5a37 100644 --- a/poppler/UnicodeMapTables.h +++ b/poppler/UnicodeMapTables.h @@ -14,12 +14,15 @@ // under GPL version 2 or later // // Copyright (C) 2019 Volker Krause +// Copyright (C) 2024 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== +#include "UnicodeMap.h" + static const UnicodeMapRange latin1UnicodeMapRanges[] = { { 0x000a, 0x000a, 0x0a, 1 }, { 0x000c, 0x000d, 0x0c, 1 }, { 0x0020, 0x007e, 0x20, 1 }, { 0x00a0, 0x00a0, 0x20, 1 }, { 0x00a1, 0x00ac, 0xa1, 1 }, { 0x00ae, 0x00ff, 0xae, 1 }, { 0x010c, 0x010c, 0x43, 1 }, { 0x010d, 0x010d, 0x63, 1 }, { 0x0131, 0x0131, 0x69, 1 }, { 0x0141, 0x0141, 0x4c, 1 }, { 0x0142, 0x0142, 0x6c, 1 }, { 0x0152, 0x0152, 0x4f45, 2 }, { 0x0153, 0x0153, 0x6f65, 2 }, { 0x0160, 0x0160, 0x53, 1 }, diff --git a/poppler/UnicodeTypeTable.cc b/poppler/UnicodeTypeTable.cc index 2b19477..55c30e8 100644 --- a/poppler/UnicodeTypeTable.cc +++ b/poppler/UnicodeTypeTable.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2006, 2007 Ed Catmur // Copyright (C) 2007 Jeff Muizelaar -// Copyright (C) 2008, 2016 Albert Astals Cid +// Copyright (C) 2008, 2016, 2024 Albert Astals Cid // Copyright (C) 2012 Adrian Johnson // Copyright (C) 2016 Khaled Hosny // Copyright (C) 2019 Adriaan de Groot @@ -637,8 +637,6 @@ Unicode unicodeToUpper(Unicode c) return c; } -#define UNICODE_LAST_CHAR 0x10FFFF -#define UNICODE_MAX_TABLE_INDEX (UNICODE_LAST_CHAR / 256 + 1) // large empty block between U+2FA1D and U+E0001 #define UNICODE_LAST_CHAR_PART1 0x2FAFF #define UNICODE_LAST_PAGE_PART1 (UNICODE_LAST_CHAR_PART1 / 256) @@ -652,7 +650,7 @@ Unicode unicodeToUpper(Unicode c) #define CC_PART2(Page, Char) ((combining_class_table_part2[Page] >= UNICODE_MAX_TABLE_INDEX) ? (combining_class_table_part2[Page] - UNICODE_MAX_TABLE_INDEX) : (cclass_data[combining_class_table_part2[Page]][Char])) -#define COMBINING_CLASS(u) (((u) <= UNICODE_LAST_CHAR_PART1) ? CC_PART1((u) / 256, (u) % 256) : (((u) >= UNICODE_PART2_START && (u) <= UNICODE_LAST_CHAR) ? CC_PART2(((u)-UNICODE_PART2_START) / 256, (u) % 256) : 0)) +#define COMBINING_CLASS(u) (((u) <= UNICODE_LAST_CHAR_PART1) ? CC_PART1((u) / 256, (u) % 256) : (((u) >= UNICODE_PART2_START && (u) <= UNICODE_LAST_CHAR) ? CC_PART2(((u) - UNICODE_PART2_START) / 256, (u) % 256) : 0)) // Write the compatibility decomposition of @u into @buf, returning the number // of characters written. @buf may be NULL, in which case the length of the @@ -754,10 +752,10 @@ static bool combine(Unicode base, Unicode add, Unicode *out) #define HANGUL_IS_V(u) (((u) >= HANGUL_V_BASE) && ((u) < HANGUL_V_BASE + HANGUL_V_COUNT)) #define HANGUL_IS_T(u) (((u) >= HANGUL_T_BASE) && ((u) < HANGUL_T_BASE + HANGUL_T_COUNT)) #define HANGUL_IS_SYLLABLE(u) (((u) >= HANGUL_S_BASE) && ((u) < HANGUL_S_BASE + HANGUL_S_COUNT)) -#define HANGUL_SYLLABLE_IS_LV(u) (((u)-HANGUL_S_BASE) % HANGUL_T_COUNT == 0) +#define HANGUL_SYLLABLE_IS_LV(u) (((u) - HANGUL_S_BASE) % HANGUL_T_COUNT == 0) #define IS_HANGUL(u) (HANGUL_IS_L(u) || HANGUL_IS_V(u) || HANGUL_IS_T(u) || HANGUL_IS_SYLLABLE(u)) -#define HANGUL_COMPOSE_L_V(l, v) (HANGUL_S_BASE + (HANGUL_T_COUNT * (((v)-HANGUL_V_BASE) + (HANGUL_V_COUNT * ((l)-HANGUL_L_BASE))))) -#define HANGUL_COMPOSE_LV_T(lv, t) ((lv) + ((t)-HANGUL_T_BASE)) +#define HANGUL_COMPOSE_L_V(l, v) (HANGUL_S_BASE + (HANGUL_T_COUNT * (((v) - HANGUL_V_BASE) + (HANGUL_V_COUNT * ((l) - HANGUL_L_BASE))))) +#define HANGUL_COMPOSE_LV_T(lv, t) ((lv) + ((t) - HANGUL_T_BASE)) // Converts Unicode string @in of length @len to its normalization in form // NFKC (compatibility decomposition + canonical composition). The length of diff --git a/poppler/UnicodeTypeTable.h b/poppler/UnicodeTypeTable.h index 85d88d6..f3e5f76 100644 --- a/poppler/UnicodeTypeTable.h +++ b/poppler/UnicodeTypeTable.h @@ -17,7 +17,7 @@ // Copyright (C) 2012 Adrian Johnson // Copyright (C) 2016 Khaled Hosny // Copyright (C) 2019 Adriaan de Groot -// Copyright (C) 2019 Albert Astals Cid +// Copyright (C) 2019, 2024 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -30,6 +30,9 @@ #include "CharTypes.h" #include "poppler_private_export.h" +#define UNICODE_LAST_CHAR 0x10FFFF +#define UNICODE_MAX_TABLE_INDEX (UNICODE_LAST_CHAR / 256 + 1) + extern bool unicodeTypeL(Unicode c); extern bool unicodeTypeR(Unicode c); diff --git a/poppler/ViewerPreferences.cc b/poppler/ViewerPreferences.cc index 2fd8098..621232a 100644 --- a/poppler/ViewerPreferences.cc +++ b/poppler/ViewerPreferences.cc @@ -111,4 +111,4 @@ ViewerPreferences::ViewerPreferences(Dict *prefDict) } } -ViewerPreferences::~ViewerPreferences() { } +ViewerPreferences::~ViewerPreferences() = default; diff --git a/poppler/ViewerPreferences.h b/poppler/ViewerPreferences.h index 24a814c..1545ce8 100644 --- a/poppler/ViewerPreferences.h +++ b/poppler/ViewerPreferences.h @@ -6,13 +6,15 @@ // // Copyright 2011 Pino Toscano // Copyright 2019 Marek Kasik -// Copyright 2021, 2022 Albert Astals Cid +// Copyright 2021, 2022, 2024 Albert Astals Cid // //======================================================================== #ifndef VIEWERPREFERENCES_H #define VIEWERPREFERENCES_H +#include "poppler_private_export.h" + #include class Dict; @@ -21,7 +23,7 @@ class Dict; // ViewerPreferences //------------------------------------------------------------------------ -class ViewerPreferences +class POPPLER_PRIVATE_EXPORT ViewerPreferences { public: enum NonFullScreenPageMode diff --git a/poppler/XRef.cc b/poppler/XRef.cc index 601ecd6..279123c 100644 --- a/poppler/XRef.cc +++ b/poppler/XRef.cc @@ -15,7 +15,7 @@ // // Copyright (C) 2005 Dan Sheridan // Copyright (C) 2005 Brad Hards -// Copyright (C) 2006, 2008, 2010, 2012-2014, 2016-2022 Albert Astals Cid +// Copyright (C) 2006, 2008, 2010, 2012-2014, 2016-2024 Albert Astals Cid // Copyright (C) 2007-2008 Julien Rebetez // Copyright (C) 2007 Carlos Garcia Campos // Copyright (C) 2009, 2010 Ilya Gorenbein @@ -32,6 +32,11 @@ // Copyright (C) 2010 William Bader // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2023 Ilaï Deutel +// Copyright (C) 2023 Even Rouault +// Copyright (C) 2024 Nelson Benítez León +// Copyright (C) 2024 Vincent Lefevre // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -157,12 +162,14 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA, int recursion) error(errSyntaxError, -1, "Too many objects in an object stream"); return; } + if (!objStr.streamReset()) { + return; + } objs = new Object[nObjects]; objNums = (int *)gmallocn(nObjects, sizeof(int)); offsets = (Goffset *)gmallocn(nObjects, sizeof(Goffset)); // parse the header: object numbers and offsets - objStr.streamReset(); str = new EmbedStream(objStr.getStream(), Object(objNull), true, first); parser = new Parser(xref, str, false); for (i = 0; i < nObjects; ++i) { @@ -253,9 +260,11 @@ XRef::XRef() : objStrs { 5 } permFlags = defPermFlags; ownerPasswordOk = false; rootNum = -1; + rootGen = -1; strOwner = false; xrefReconstructed = false; encAlgorithm = cryptNone; + keyLength = 0; } XRef::XRef(const Object *trailerDictA) : XRef {} @@ -415,22 +424,34 @@ XRef *XRef::copy() const int XRef::reserve(int newSize) { if (newSize > capacity) { - - int realNewSize; - for (realNewSize = capacity ? 2 * capacity : 1024; newSize > realNewSize && realNewSize > 0; realNewSize <<= 1) { - ; + int newCapacity = 1024; + if (capacity) { + if (capacity <= INT_MAX / 2) { + newCapacity = capacity * 2; + } else { + newCapacity = newSize; + } } - if ((realNewSize < 0) || (realNewSize >= INT_MAX / (int)sizeof(XRefEntry))) { + while (newSize > newCapacity) { + if (newCapacity > INT_MAX / 2) { + std::fputs("Too large XRef size\n", stderr); + return 0; + } + newCapacity *= 2; + } + if (newCapacity >= INT_MAX / (int)sizeof(XRefEntry)) { + std::fputs("Too large XRef size\n", stderr); return 0; } - void *p = greallocn_checkoverflow(entries, realNewSize, sizeof(XRefEntry)); + void *p = grealloc(entries, newCapacity * sizeof(XRefEntry), + /* checkoverflow=*/true); if (p == nullptr) { return 0; } entries = (XRefEntry *)p; - capacity = realNewSize; + capacity = newCapacity; } return capacity; @@ -729,7 +750,10 @@ bool XRef::readXRefStream(Stream *xrefStr, Goffset *pos) return false; } - xrefStr->reset(); + if (!xrefStr->reset()) { + return false; + } + const Object &idx = dict->lookupNF("Index"); if (idx.isArray()) { for (int i = 0; i + 1 < idx.arrayGetLength(); i += 2) { @@ -888,7 +912,9 @@ bool XRef::constructXRef(bool *wasReconstructed, bool needCatalogDict) xrefReconstructedCb(); } - str->reset(); + if (!str->reset()) { + return false; + } while (true) { pos = str->getPos(); if (!str->getLine(buf, 256)) { @@ -919,7 +945,7 @@ bool XRef::constructXRef(bool *wasReconstructed, bool needCatalogDict) Object newTrailerDict = parser->getObj(); if (newTrailerDict.isDict()) { const Object &obj = newTrailerDict.dictLookupNF("Root"); - if (obj.isRef() && (!gotRoot || !needCatalogDict) && rootNum != obj.getRefNum()) { + if (obj.isRef() && (!gotRoot || !needCatalogDict)) { rootNum = obj.getRefNum(); rootGen = obj.getRefGen(); trailerDict = newTrailerDict.copy(); @@ -1170,6 +1196,16 @@ Object XRef::fetch(int num, int gen, int recursion, Goffset *endPos) Object obj1, obj2, obj3; xrefLocker(); + + const Ref ref = { num, gen }; + + if (!refsBeingFetched.insert(ref)) { + return Object(objNull); + } + + // Will remove ref from refsBeingFetched once it's destroyed, i.e. the function returns + RefRecursionCheckerRemover remover(refsBeingFetched, ref); + // check for bogus ref - this can happen in corrupted PDF files if (num < 0 || num >= size) { goto err; @@ -1262,13 +1298,13 @@ Object XRef::fetch(int num, int gen, int recursion, Goffset *endPos) } } if (xrefHasChanges) { - error(errInternal, -1, "xref num {0:d} not found but needed, document has changes, reconstruct aborted\n", num); + error(errInternal, -1, "xref num {0:d} not found but needed, document has changes, reconstruct aborted", num); // pretend we constructed the xref, otherwise we will do this check again and again xrefReconstructed = true; return Object(objNull); } - error(errInternal, -1, "xref num {0:d} not found but needed, try to reconstruct\n", num); + error(errInternal, -1, "xref num {0:d} not found but needed, try to reconstruct", num); rootNum = -1; constructXRef(&xrefReconstructed); return fetch(num, gen, ++recursion, endPos); @@ -1421,10 +1457,13 @@ void XRef::setModifiedObject(const Object *o, Ref r) { xrefLocker(); if (r.num < 0 || r.num >= size) { - error(errInternal, -1, "XRef::setModifiedObject on unknown ref: {0:d}, {1:d}\n", r.num, r.gen); + error(errInternal, -1, "XRef::setModifiedObject on unknown ref: {0:d}, {1:d}", r.num, r.gen); return; } XRefEntry *e = getEntry(r.num); + if (unlikely(e->type == xrefEntryFree)) { + error(errInternal, -1, "XRef::setModifiedObject on ref: {0:d}, {1:d} that is marked as free. This will cause a memory leak", r.num, r.gen); + } e->obj = o->copy(); e->setFlag(XRefEntry::Updated, true); setModified(); @@ -1466,7 +1505,7 @@ void XRef::removeIndirectObject(Ref r) { xrefLocker(); if (r.num < 0 || r.num >= size) { - error(errInternal, -1, "XRef::removeIndirectObject on unknown ref: {0:d}, {1:d}\n", r.num, r.gen); + error(errInternal, -1, "XRef::removeIndirectObject on unknown ref: {0:d}, {1:d}", r.num, r.gen); return; } XRefEntry *e = getEntry(r.num); @@ -1482,24 +1521,31 @@ void XRef::removeIndirectObject(Ref r) setModified(); } -Ref XRef::addStreamObject(Dict *dict, char *buffer, const Goffset bufferSize) +Ref XRef::addStreamObject(Dict *dict, char *buffer, const Goffset bufferSize, StreamCompression compression) { dict->add("Length", Object((int)bufferSize)); AutoFreeMemStream *stream = new AutoFreeMemStream(buffer, 0, bufferSize, Object(dict)); stream->setFilterRemovalForbidden(true); + switch (compression) { + case StreamCompression::None:; + break; + case StreamCompression::Compress: + stream->getDict()->add("Filter", Object(objName, "FlateDecode")); + break; + } return addIndirectObject(Object((Stream *)stream)); } -Ref XRef::addStreamObject(Dict *dict, uint8_t *buffer, const Goffset bufferSize) +Ref XRef::addStreamObject(Dict *dict, uint8_t *buffer, const Goffset bufferSize, StreamCompression compression) { - return addStreamObject(dict, (char *)buffer, bufferSize); + return addStreamObject(dict, (char *)buffer, bufferSize, compression); } void XRef::writeXRef(XRef::XRefWriter *writer, bool writeAllEntries) { // create free entries linked-list if (getEntry(0)->gen != 65535) { - error(errInternal, -1, "XRef::writeXRef, entry 0 of the XRef is invalid (gen != 65535)\n"); + error(errInternal, -1, "XRef::writeXRef, entry 0 of the XRef is invalid (gen != 65535)"); } int lastFreeEntry = 0; for (int i = 0; i < size; i++) { diff --git a/poppler/XRef.h b/poppler/XRef.h index e2b2ca8..739b87b 100644 --- a/poppler/XRef.h +++ b/poppler/XRef.h @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Brad Hards -// Copyright (C) 2006, 2008, 2010-2013, 2017-2022 Albert Astals Cid +// Copyright (C) 2006, 2008, 2010-2013, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2007-2008 Julien Rebetez // Copyright (C) 2007 Carlos Garcia Campos // Copyright (C) 2010 Ilya Gorenbein @@ -27,6 +27,7 @@ // Copyright (C) 2018 Marek Kasik // Copyright (C) 2021 Mahmoud Khalil // Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. +// Copyright (C) 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -97,6 +98,13 @@ struct XRefEntry } }; +// How to compress the a added stream +enum class StreamCompression +{ + None, /* No compression */ + Compress, /* Compresses the stream */ +}; + class POPPLER_PRIVATE_EXPORT XRef { public: @@ -212,9 +220,11 @@ class POPPLER_PRIVATE_EXPORT XRef // Adds a stream object using AutoFreeMemStream. // The function takes ownership over dict and buffer. // The buffer should be created using gmalloc(). + // For stream compression, if the data is already compressed + // don't compress again. If it is not compressed, use compress (Flate / zlib) // Returns ref to a new object. - Ref addStreamObject(Dict *dict, char *buffer, const Goffset bufferSize); - Ref addStreamObject(Dict *dict, uint8_t *buffer, const Goffset bufferSize); + Ref addStreamObject(Dict *dict, char *buffer, const Goffset bufferSize, StreamCompression compression); + Ref addStreamObject(Dict *dict, uint8_t *buffer, const Goffset bufferSize, StreamCompression compression); // Output XRef table to stream void writeTableToFile(OutStream *outStr, bool writeAllEntries); @@ -259,6 +269,8 @@ class POPPLER_PRIVATE_EXPORT XRef mutable std::recursive_mutex mutex; std::function xrefReconstructedCb; + RefRecursionChecker refsBeingFetched; + int reserve(int newSize); int resize(int newSize); bool readXRef(Goffset *pos, std::vector *followedXRefStm, std::vector *xrefStreamObjsNum); diff --git a/poppler/libpoppler.map.in b/poppler/libpoppler.map.in new file mode 100644 index 0000000..8a0ee01 --- /dev/null +++ b/poppler/libpoppler.map.in @@ -0,0 +1,4 @@ +POPPLER_@POPPLER_SOVERSION_NUMBER@ { + global: + *; +}; diff --git a/poppler/poppler-config.h.cmake b/poppler/poppler-config.h.cmake index 733c427..3261c75 100644 --- a/poppler/poppler-config.h.cmake +++ b/poppler/poppler-config.h.cmake @@ -74,11 +74,6 @@ #cmakedefine ENABLE_LIBPNG 1 #endif -/* Use zlib instead of builtin zlib decoder. */ -#ifndef ENABLE_ZLIB -#cmakedefine ENABLE_ZLIB 1 -#endif - /* Define to 1 if you have the header file, and it defines `DIR'. */ #ifndef HAVE_DIRENT_H @@ -122,7 +117,7 @@ //------------------------------------------------------------------------ // copyright notice -#define popplerCopyright "Copyright 2005-2022 The Poppler Developers - http://poppler.freedesktop.org" +#define popplerCopyright "Copyright 2005-2025 The Poppler Developers - http://poppler.freedesktop.org" #define xpdfCopyright "Copyright 1996-2011, 2022 Glyph & Cog, LLC" //------------------------------------------------------------------------ diff --git a/qt5/CMakeLists.txt b/qt5/CMakeLists.txt index 64e8160..431d2d3 100644 --- a/qt5/CMakeLists.txt +++ b/qt5/CMakeLists.txt @@ -5,7 +5,7 @@ if(ENABLE_QT_STRICT_ITERATORS) add_definitions(-DQT_STRICT_ITERATORS) endif() -add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050900) +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050E00) add_definitions(-DQT_NO_DEPRECATED_WARNINGS) add_subdirectory(src) diff --git a/qt5/demos/abstractinfodock.cpp b/qt5/demos/abstractinfodock.cpp index 8a2e216..91eeee4 100644 --- a/qt5/demos/abstractinfodock.cpp +++ b/qt5/demos/abstractinfodock.cpp @@ -23,7 +23,7 @@ AbstractInfoDock::AbstractInfoDock(QWidget *parent) : QDockWidget(parent), m_fil connect(this, &AbstractInfoDock::visibilityChanged, this, &AbstractInfoDock::slotVisibilityChanged); } -AbstractInfoDock::~AbstractInfoDock() { } +AbstractInfoDock::~AbstractInfoDock() = default; void AbstractInfoDock::documentLoaded() { diff --git a/qt5/demos/abstractinfodock.h b/qt5/demos/abstractinfodock.h index 9dd2576..6a04b2d 100644 --- a/qt5/demos/abstractinfodock.h +++ b/qt5/demos/abstractinfodock.h @@ -39,10 +39,9 @@ class AbstractInfoDock : public QDockWidget, public DocumentObserver protected: virtual void fillInfo() = 0; -private Q_SLOTS: +private: void slotVisibilityChanged(bool visible); -private: bool m_filled; }; diff --git a/qt5/demos/documentobserver.cpp b/qt5/demos/documentobserver.cpp index 5bf41ac..5e3b7a1 100644 --- a/qt5/demos/documentobserver.cpp +++ b/qt5/demos/documentobserver.cpp @@ -22,7 +22,7 @@ DocumentObserver::DocumentObserver() : m_viewer(nullptr) { } -DocumentObserver::~DocumentObserver() { } +DocumentObserver::~DocumentObserver() = default; Poppler::Document *DocumentObserver::document() const { diff --git a/qt5/demos/embeddedfiles.cpp b/qt5/demos/embeddedfiles.cpp index aa7da46..b22421c 100644 --- a/qt5/demos/embeddedfiles.cpp +++ b/qt5/demos/embeddedfiles.cpp @@ -32,7 +32,7 @@ EmbeddedFilesDock::EmbeddedFilesDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -EmbeddedFilesDock::~EmbeddedFilesDock() { } +EmbeddedFilesDock::~EmbeddedFilesDock() = default; void EmbeddedFilesDock::fillInfo() { diff --git a/qt5/demos/fonts.cpp b/qt5/demos/fonts.cpp index 6982bbf..53f5b6a 100644 --- a/qt5/demos/fonts.cpp +++ b/qt5/demos/fonts.cpp @@ -37,7 +37,7 @@ FontsDock::FontsDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -FontsDock::~FontsDock() { } +FontsDock::~FontsDock() = default; void FontsDock::fillInfo() { diff --git a/qt5/demos/info.cpp b/qt5/demos/info.cpp index a3d3533..10598c2 100644 --- a/qt5/demos/info.cpp +++ b/qt5/demos/info.cpp @@ -32,7 +32,7 @@ InfoDock::InfoDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -InfoDock::~InfoDock() { } +InfoDock::~InfoDock() = default; void InfoDock::fillInfo() { diff --git a/qt5/demos/main_viewer.cpp b/qt5/demos/main_viewer.cpp index ad2f388..1428cad 100644 --- a/qt5/demos/main_viewer.cpp +++ b/qt5/demos/main_viewer.cpp @@ -29,5 +29,5 @@ int main(int argc, char *argv[]) if (args.count() > 1) { viewer->loadDocument(args.at(1)); } - return app.exec(); + return QApplication::exec(); } diff --git a/qt5/demos/metadata.cpp b/qt5/demos/metadata.cpp index 5630db2..973d47d 100644 --- a/qt5/demos/metadata.cpp +++ b/qt5/demos/metadata.cpp @@ -31,7 +31,7 @@ MetadataDock::MetadataDock(QWidget *parent) : AbstractInfoDock(parent) m_edit->setReadOnly(true); } -MetadataDock::~MetadataDock() { } +MetadataDock::~MetadataDock() = default; void MetadataDock::fillInfo() { diff --git a/qt5/demos/navigationtoolbar.cpp b/qt5/demos/navigationtoolbar.cpp index 74b3240..2bbe8c2 100644 --- a/qt5/demos/navigationtoolbar.cpp +++ b/qt5/demos/navigationtoolbar.cpp @@ -27,13 +27,13 @@ NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) { - m_firstAct = addAction(tr("First"), this, SLOT(slotGoFirst())); - m_prevAct = addAction(tr("Previous"), this, SLOT(slotGoPrev())); + m_firstAct = addAction(tr("First"), this, &NavigationToolBar::slotGoFirst); + m_prevAct = addAction(tr("Previous"), this, &NavigationToolBar::slotGoPrev); m_pageCombo = new QComboBox(this); connect(m_pageCombo, qOverload(&QComboBox::activated), this, &NavigationToolBar::slotComboActivated); addWidget(m_pageCombo); - m_nextAct = addAction(tr("Next"), this, SLOT(slotGoNext())); - m_lastAct = addAction(tr("Last"), this, SLOT(slotGoLast())); + m_nextAct = addAction(tr("Next"), this, &NavigationToolBar::slotGoNext); + m_lastAct = addAction(tr("Last"), this, &NavigationToolBar::slotGoLast); addSeparator(); @@ -67,7 +67,7 @@ NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) documentClosed(); } -NavigationToolBar::~NavigationToolBar() { } +NavigationToolBar::~NavigationToolBar() = default; void NavigationToolBar::documentLoaded() { diff --git a/qt5/demos/navigationtoolbar.h b/qt5/demos/navigationtoolbar.h index f312d28..23ffeeb 100644 --- a/qt5/demos/navigationtoolbar.h +++ b/qt5/demos/navigationtoolbar.h @@ -44,7 +44,7 @@ class NavigationToolBar : public QToolBar, public DocumentObserver void zoomChanged(qreal value); // NOLINT(readability-inconsistent-declaration-parameter-name) void rotationChanged(int rotation); // NOLINT(readability-inconsistent-declaration-parameter-name) -private Q_SLOTS: +private: void slotGoFirst(); void slotGoPrev(); void slotGoNext(); @@ -53,7 +53,6 @@ private Q_SLOTS: void slotZoomComboChanged(const QString &text); void slotRotationComboChanged(int idx); -private: QAction *m_firstAct; QAction *m_prevAct; QComboBox *m_pageCombo; diff --git a/qt5/demos/optcontent.cpp b/qt5/demos/optcontent.cpp index 2febed8..0f34ca8 100644 --- a/qt5/demos/optcontent.cpp +++ b/qt5/demos/optcontent.cpp @@ -30,7 +30,7 @@ OptContentDock::OptContentDock(QWidget *parent) : AbstractInfoDock(parent) m_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -OptContentDock::~OptContentDock() { } +OptContentDock::~OptContentDock() = default; void OptContentDock::documentLoaded() { diff --git a/qt5/demos/optcontent.h b/qt5/demos/optcontent.h index 9da451b..a9893ac 100644 --- a/qt5/demos/optcontent.h +++ b/qt5/demos/optcontent.h @@ -38,10 +38,9 @@ class OptContentDock : public AbstractInfoDock protected: void fillInfo() override; -private Q_SLOTS: +private: void reloadImage(); -private: QTreeView *m_view; }; diff --git a/qt5/demos/pageview.cpp b/qt5/demos/pageview.cpp index 3acfdb3..7c98fec 100644 --- a/qt5/demos/pageview.cpp +++ b/qt5/demos/pageview.cpp @@ -36,7 +36,7 @@ PageView::PageView(QWidget *parent) : QScrollArea(parent), m_zoom(1.0), m_rotati setWidget(m_imageLabel); } -PageView::~PageView() { } +PageView::~PageView() = default; void PageView::documentLoaded() { } diff --git a/qt5/demos/pageview.h b/qt5/demos/pageview.h index c7446d4..4984f81 100644 --- a/qt5/demos/pageview.h +++ b/qt5/demos/pageview.h @@ -39,7 +39,6 @@ class PageView : public QScrollArea, public DocumentObserver void documentClosed() override; void pageChanged(int page) override; -public Q_SLOTS: void slotZoomChanged(qreal value); void slotRotationChanged(int value); diff --git a/qt5/demos/permissions.cpp b/qt5/demos/permissions.cpp index f7d99cb..6d6f1c6 100644 --- a/qt5/demos/permissions.cpp +++ b/qt5/demos/permissions.cpp @@ -30,7 +30,7 @@ PermissionsDock::PermissionsDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -PermissionsDock::~PermissionsDock() { } +PermissionsDock::~PermissionsDock() = default; void PermissionsDock::fillInfo() { diff --git a/qt5/demos/thumbnails.cpp b/qt5/demos/thumbnails.cpp index 08fbdba..6816e6a 100644 --- a/qt5/demos/thumbnails.cpp +++ b/qt5/demos/thumbnails.cpp @@ -37,7 +37,7 @@ ThumbnailsDock::ThumbnailsDock(QWidget *parent) : AbstractInfoDock(parent) connect(m_list, &QListWidget::itemActivated, this, &ThumbnailsDock::slotItemActivated); } -ThumbnailsDock::~ThumbnailsDock() { } +ThumbnailsDock::~ThumbnailsDock() = default; void ThumbnailsDock::fillInfo() { diff --git a/qt5/demos/thumbnails.h b/qt5/demos/thumbnails.h index a568df6..7e46d4a 100644 --- a/qt5/demos/thumbnails.h +++ b/qt5/demos/thumbnails.h @@ -39,10 +39,9 @@ class ThumbnailsDock : public AbstractInfoDock protected: void fillInfo() override; -private Q_SLOTS: +private: void slotItemActivated(QListWidgetItem *item); -private: QListWidget *m_list; }; diff --git a/qt5/demos/toc.cpp b/qt5/demos/toc.cpp index 4173e14..136256b 100644 --- a/qt5/demos/toc.cpp +++ b/qt5/demos/toc.cpp @@ -127,7 +127,7 @@ TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent) m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -TocDock::~TocDock() { } +TocDock::~TocDock() = default; void TocDock::expandItemModels(const QModelIndex &parent) { diff --git a/qt5/demos/viewer.h b/qt5/demos/viewer.h index 752a00e..cb2b98f 100644 --- a/qt5/demos/viewer.h +++ b/qt5/demos/viewer.h @@ -46,7 +46,7 @@ class PdfViewer : public QMainWindow void loadDocument(const QString &file); void closeDocument(); -private Q_SLOTS: +private: void slotOpenFile(); void slotSaveCopy(); void slotAbout(); @@ -55,7 +55,6 @@ private Q_SLOTS: void slotToggleGfxAA(bool value); void slotRenderBackend(QAction *act); -private: void setPage(int page); int page() const; void xrefReconstructedHandler(Poppler::Document *doc); diff --git a/qt5/src/CMakeLists.txt b/qt5/src/CMakeLists.txt index 638505e..d49da0f 100644 --- a/qt5/src/CMakeLists.txt +++ b/qt5/src/CMakeLists.txt @@ -5,10 +5,6 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) - configure_file(poppler-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h @ONLY) set(poppler_qt5_SRCS @@ -38,7 +34,7 @@ set(poppler_qt5_SRCS ) add_library(poppler-qt5 ${poppler_qt5_SRCS}) generate_export_header(poppler-qt5 BASE_NAME poppler-qt5 EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h") -set_target_properties(poppler-qt5 PROPERTIES VERSION 1.31.0 SOVERSION 1) +set_target_properties(poppler-qt5 PROPERTIES VERSION 1.38.0 SOVERSION 1) if(MINGW AND BUILD_SHARED_LIBS) get_target_property(POPPLER_QT5_SOVERSION poppler-qt5 SOVERSION) set_target_properties(poppler-qt5 PROPERTIES SUFFIX "-${POPPLER_QT5_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") @@ -49,10 +45,11 @@ if (ENABLE_NSS3) endif() if(USE_CMS) target_link_libraries(poppler-qt5 poppler ${LCMS2_LIBRARIES}) + target_include_directories(poppler-qt5 SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) endif() install(TARGETS poppler-qt5 RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES +set(poppler_qt5_all_install_headers poppler-qt5.h poppler-link.h poppler-annotation.h @@ -62,5 +59,15 @@ install(FILES poppler-media.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h - DESTINATION include/poppler/qt5) - +) +if (${CMAKE_VERSION} VERSION_LESS "3.23.0") + install(FILES ${poppler_qt5_all_install_headers} DESTINATION include/poppler/qt5) +else() + target_sources(poppler-qt5 + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR} + FILES ${poppler_qt5_all_install_headers} + ) + install(TARGETS poppler-qt5 FILE_SET HEADERS DESTINATION include/poppler/qt5) +endif() diff --git a/qt5/src/Doxyfile b/qt5/src/Doxyfile index 0a4b33e..6da3d86 100644 --- a/qt5/src/Doxyfile +++ b/qt5/src/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = "Poppler Qt5" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 22.12.0 +PROJECT_NUMBER = 25.03.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/qt5/src/Mainpage.dox b/qt5/src/Mainpage.dox index ea10589..bc3becb 100644 --- a/qt5/src/Mainpage.dox +++ b/qt5/src/Mainpage.dox @@ -33,6 +33,25 @@ simply need to add the following line to their C++ source files: #include @endcode +To use the Qt5 interface on Android, there is an additional step - you must place the following font files in the assets/share/fonts directory of the Android APK: + + - NimbusMonoPS-Regular.otf + - NimbusMonoPS-Bold.otf + - NimbusMonoPS-BoldItalic.otf + - NimbusMonoPS-Italic.otf + - NimbusSans-Regular.otf + - NimbusSans-Bold.otf + - NimbusSans-BoldItalic.otf + - NimbusSans-Italic.otf + - StandardSymbolsPS.otf + - NimbusRoman-Bold.otf + - imbusRoman-BoldItalic.otf + - NimbusRoman-Italic.otf + - NimbusRoman-Regular.otf + - D050000L.otf + +These are used as substitute fonts for the base-14 fonts, and this step is required in order to reliably display documents with unembedded fonts. You can easily find these font files included within GhostScript. + A PDF document can then be loaded as follows: @code QString filename; diff --git a/qt5/src/QPainterOutputDev.cc b/qt5/src/QPainterOutputDev.cc index b017bab..7a483c2 100644 --- a/qt5/src/QPainterOutputDev.cc +++ b/qt5/src/QPainterOutputDev.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Brad Hards -// Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Petr Gajdos @@ -27,6 +27,7 @@ // Copyright (C) 2017, 2022 Adrian Johnson // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -162,10 +163,6 @@ QPainterOutputDev::QPainterOutputDev(QPainter *painter) : m_lastTransparencyGrou QPainterOutputDev::~QPainterOutputDev() { - for (auto &codeToGID : m_codeToGIDCache) { - gfree(const_cast(codeToGID.second)); - } - FT_Done_FreeType(m_ftLibrary); } @@ -174,10 +171,9 @@ void QPainterOutputDev::startDoc(PDFDoc *doc) xref = doc->getXRef(); m_doc = doc; - for (auto &codeToGID : m_codeToGIDCache) { - gfree(const_cast(codeToGID.second)); - } m_codeToGIDCache.clear(); + m_codeToGIDStack = {}; + m_codeToGID = nullptr; } void QPainterOutputDev::startPage(int pageNum, GfxState *state, XRef *) { } @@ -515,7 +511,7 @@ void QPainterOutputDev::updateFont(GfxState *state) if (codeToGIDIt != m_codeToGIDCache.end()) { - m_codeToGID = codeToGIDIt->second; + m_codeToGID = &(codeToGIDIt->second); } else { @@ -547,16 +543,15 @@ void QPainterOutputDev::updateFont(GfxState *state) case fontType1C: case fontType1COT: { // Load the font face using FreeType - const int faceIndex = 0; // We always load the zero-th face from a font FT_Face freeTypeFace; if (fontLoc->locType != gfxFontLocEmbedded) { - if (ft_new_face_from_file(m_ftLibrary, fontLoc->path.c_str(), faceIndex, &freeTypeFace)) { + if (ft_new_face_from_file(m_ftLibrary, fontLoc->path.c_str(), fontLoc->fontNum, &freeTypeFace)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } } else { - if (FT_New_Memory_Face(m_ftLibrary, (const FT_Byte *)fontBuffer->data(), fontBuffer->size(), faceIndex, &freeTypeFace)) { + if (FT_New_Memory_Face(m_ftLibrary, (const FT_Byte *)fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum, &freeTypeFace)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } @@ -564,9 +559,9 @@ void QPainterOutputDev::updateFont(GfxState *state) const char *name; - int *codeToGID = (int *)gmallocn(256, sizeof(int)); + std::vector codeToGID; + codeToGID.resize(256, 0); for (int i = 0; i < 256; ++i) { - codeToGID[i] = 0; if ((name = ((const char **)((Gfx8BitFont *)gfxFont.get())->getEncoding())[i])) { codeToGID[i] = (int)FT_Get_Name_Index(freeTypeFace, (char *)name); if (codeToGID[i] == 0) { @@ -580,28 +575,27 @@ void QPainterOutputDev::updateFont(GfxState *state) FT_Done_Face(freeTypeFace); - m_codeToGIDCache[id] = codeToGID; + m_codeToGIDCache[id] = std::move(codeToGID); break; } case fontTrueType: case fontTrueTypeOT: { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); - m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : nullptr; + m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : std::vector {}; break; } case fontCIDType0: case fontCIDType0C: { - int *cidToGIDMap = nullptr; - int nCIDs = 0; + std::vector cidToGIDMap; // check for a CFF font if (!m_useCIDs) { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? std::unique_ptr(FoFiType1C::load(fontLoc->path.c_str())) : std::unique_ptr(FoFiType1C::make(fontBuffer->data(), fontBuffer->size())); - cidToGIDMap = (ff) ? ff->getCIDToGIDMap(&nCIDs) : nullptr; + cidToGIDMap = (ff) ? ff->getCIDToGIDMap() : std::vector {}; } m_codeToGIDCache[id] = cidToGIDMap; @@ -609,45 +603,37 @@ void QPainterOutputDev::updateFont(GfxState *state) break; } case fontCIDType0COT: { - int *codeToGID = nullptr; + std::vector codeToGID; - if (((GfxCIDFont *)gfxFont.get())->getCIDToGID()) { - int codeToGIDLen = ((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont.get())->getCIDToGID(), codeToGIDLen * sizeof(int)); + if (((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCIDToGID(); } - int *cidToGIDMap = nullptr; - int nCIDs = 0; + std::vector cidToGIDMap; - if (!codeToGID && !m_useCIDs) { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + if (codeToGID.empty() && !m_useCIDs) { + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); if (ff && ff->isOpenTypeCFF()) { - cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + cidToGIDMap = ff->getCIDToGIDMap(); } } - m_codeToGIDCache[id] = codeToGID ? codeToGID : cidToGIDMap; + m_codeToGIDCache[id] = !codeToGID.empty() ? std::move(codeToGID) : std::move(cidToGIDMap); break; } case fontCIDType2: case fontCIDType2OT: { - int *codeToGID = nullptr; - int codeToGIDLen = 0; - if (((GfxCIDFont *)gfxFont.get())->getCIDToGID()) { - codeToGIDLen = ((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen(); - if (codeToGIDLen) { - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont.get())->getCIDToGID(), codeToGIDLen * sizeof(int)); - } + std::vector codeToGID; + if (((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCIDToGID(); } else { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); if (!ff) { return; } - codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get(), &codeToGIDLen); + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get()); } m_codeToGIDCache[id] = codeToGID; @@ -659,7 +645,7 @@ void QPainterOutputDev::updateFont(GfxState *state) return; } - m_codeToGID = m_codeToGIDCache[id]; + m_codeToGID = &m_codeToGIDCache[id]; } } @@ -913,7 +899,7 @@ void QPainterOutputDev::drawChar(GfxState *state, double x, double y, double dx, } if (!(render & 1)) { - quint32 glyphIndex = (m_codeToGID) ? m_codeToGID[code] : code; + quint32 glyphIndex = (m_codeToGID && code < m_codeToGID->size()) ? m_codeToGID->at(code) : code; QPointF glyphPosition = QPointF(x - originX, y - originY); // QGlyphRun objects can hold an entire sequence of glyphs, and it would possibly @@ -980,7 +966,10 @@ void QPainterOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, 1, // numPixelComps 1 // getBits ); - imgStr->reset(); + if (!imgStr->reset()) { + imgStr->close(); + return; + } // TODO: Would using QImage::Format_Mono be more efficient here? QImage image(width, height, QImage::Format_ARGB32); @@ -1023,7 +1012,10 @@ void QPainterOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + imgStr->close(); + return; + } image = QImage(width, height, QImage::Format_ARGB32); data = reinterpret_cast(image.bits()); @@ -1080,10 +1072,14 @@ void QPainterOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + return; + } auto maskImageStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); - maskImageStr->reset(); + if (!maskImageStr->reset()) { + return; + } QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); diff --git a/qt5/src/QPainterOutputDev.h b/qt5/src/QPainterOutputDev.h index c8dc1c0..ed17493 100644 --- a/qt5/src/QPainterOutputDev.h +++ b/qt5/src/QPainterOutputDev.h @@ -21,6 +21,7 @@ // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -192,11 +193,11 @@ class QPainterOutputDev : public OutputDev using QPainterFontID = std::pair; std::map> m_rawFontCache; std::map> m_type3FontCache; - std::map m_codeToGIDCache; + std::map> m_codeToGIDCache; // The table that maps character codes to glyph indexes - const int *m_codeToGID; - std::stack m_codeToGIDStack; + std::vector *m_codeToGID; + std::stack *> m_codeToGIDStack; FT_Library m_ftLibrary; // as of FT 2.1.8, CID fonts are indexed by CID instead of GID diff --git a/qt5/src/poppler-annotation-private.h b/qt5/src/poppler-annotation-private.h index 1f8d756..74b5014 100644 --- a/qt5/src/poppler-annotation-private.h +++ b/qt5/src/poppler-annotation-private.h @@ -5,6 +5,7 @@ * Copyright (C) 2012, 2014, 2018, 2019, Albert Astals Cid * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Mahmoud Ahmed Khalil + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -97,7 +98,7 @@ class AnnotationPrivate : public QSharedData void fillTransformationMTX(double MTX[6]) const; QRectF fromPdfRectangle(const PDFRectangle &r) const; PDFRectangle boundaryToPdfRectangle(const QRectF &r, int flags) const; - AnnotPath *toAnnotPath(const QLinkedList &l) const; + std::unique_ptr toAnnotPath(const QLinkedList &l) const; /* Scan page for annotations, parentId=0 searches for root annotations, subtypes empty means all subtypes */ static QList findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentId = -1); diff --git a/qt5/src/poppler-annotation.cc b/qt5/src/poppler-annotation.cc index e15523c..817951e 100644 --- a/qt5/src/poppler-annotation.cc +++ b/qt5/src/poppler-annotation.cc @@ -1,5 +1,5 @@ /* poppler-annotation.cc: qt interface to poppler - * Copyright (C) 2006, 2009, 2012-2015, 2018-2022 Albert Astals Cid + * Copyright (C) 2006, 2009, 2012-2015, 2018-2022, 2024 Albert Astals Cid * Copyright (C) 2006, 2008, 2010 Pino Toscano * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012-2014 Fabio D'Urso @@ -15,6 +15,8 @@ * Copyright (C) 2020 Thorsten Behrens * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021 Mahmoud Ahmed Khalil + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -170,11 +172,7 @@ void getRawDataFromQImage(const QImage &qimg, int bitsPerPixel, QByteArray *data break; case 8: case 24: -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) data->append((const char *)qimg.bits(), static_cast(qimg.sizeInBytes())); -#else - data->append((const char *)qimg.bits(), qimg.byteCount()); -#endif break; case 32: for (int line = 0; line < height; line++) { @@ -417,7 +415,7 @@ PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFla return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags); } -AnnotPath *AnnotationPrivate::toAnnotPath(const QLinkedList &list) const +std::unique_ptr AnnotationPrivate::toAnnotPath(const QLinkedList &list) const { const int count = list.size(); std::vector ac; @@ -432,7 +430,7 @@ AnnotPath *AnnotationPrivate::toAnnotPath(const QLinkedList &list) cons ac.emplace_back(x, y); } - return new AnnotPath(std::move(ac)); + return std::make_unique(std::move(ac)); } QList AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentID) @@ -945,7 +943,7 @@ class Annotation::Style::Private : public QSharedData Annotation::Style::Style() : d(new Private) { } -Annotation::Style::Style(const Style &other) : d(other.d) { } +Annotation::Style::Style(const Style &other) = default; Annotation::Style &Annotation::Style::operator=(const Style &other) { @@ -956,7 +954,7 @@ Annotation::Style &Annotation::Style::operator=(const Style &other) return *this; } -Annotation::Style::~Style() { } +Annotation::Style::~Style() = default; QColor Annotation::Style::color() const { @@ -1062,7 +1060,7 @@ class Annotation::Popup::Private : public QSharedData Annotation::Popup::Popup() : d(new Private) { } -Annotation::Popup::Popup(const Popup &other) : d(other.d) { } +Annotation::Popup::Popup(const Popup &other) = default; Annotation::Popup &Annotation::Popup::operator=(const Popup &other) { @@ -1073,7 +1071,7 @@ Annotation::Popup &Annotation::Popup::operator=(const Popup &other) return *this; } -Annotation::Popup::~Popup() { } +Annotation::Popup::~Popup() = default; int Annotation::Popup::flags() const { @@ -1127,7 +1125,7 @@ void Annotation::Popup::setText(const QString &text) Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { } -Annotation::~Annotation() { } +Annotation::~Annotation() = default; Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &annNode) : d_ptr(&dd) { @@ -1217,7 +1215,7 @@ Annotation::Annotation(AnnotationPrivate &dd, const QDomNode &annNode) : d_ptr(& } // If no segments were found use marks/spaces (old format) - if (dashArray.size() == 0) { + if (dashArray.empty()) { dashArray.append(ee.attribute(QStringLiteral("marks")).toDouble()); dashArray.append(ee.attribute(QStringLiteral("spaces")).toDouble()); } @@ -1343,7 +1341,7 @@ void Annotation::storeBaseAnnotationProperties(QDomNode &annNode, QDomDocument & psE.setAttribute(QStringLiteral("ycr"), QString::number(s.yCorners())); int marks = 3, spaces = 0; // Do not break code relying on marks/spaces - if (dashArray.size() != 0) { + if (!dashArray.empty()) { marks = (int)dashArray[0]; } if (dashArray.size() > 1) { @@ -1518,9 +1516,8 @@ void Annotation::setModificationDate(const QDateTime &date) if (d->pdfAnnot) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); - GooString *s = timeToDateString(&t); - d->pdfAnnot->setModified(s); - delete s; + std::unique_ptr s = timeToDateString(&t); + d->pdfAnnot->setModified(std::move(s)); } else { d->pdfAnnot->setModified(nullptr); } @@ -1557,9 +1554,8 @@ void Annotation::setCreationDate(const QDateTime &date) if (markupann) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); - GooString *s = timeToDateString(&t); - markupann->setDate(s); - delete s; + std::unique_ptr s = timeToDateString(&t); + markupann->setDate(std::move(s)); } else { markupann->setDate(nullptr); } @@ -1672,7 +1668,7 @@ void Annotation::setBoundary(const QRectF &boundary) if (rect == d->pdfAnnot->getRect()) { return; } - d->pdfAnnot->setRect(&rect); + d->pdfAnnot->setRect(rect); } Annotation::Style Annotation::style() const @@ -1703,11 +1699,7 @@ Annotation::Style Annotation::style() const s.setLineStyle((Annotation::LineStyle)(1 << border->getStyle())); const std::vector &dashArray = border->getDash(); -#if QT_VERSION <= QT_VERSION_CHECK(5, 14, 0) - s.setDashArray(QVector::fromStdVector(dashArray)); -#else s.setDashArray(QVector(dashArray.begin(), dashArray.end())); -#endif } AnnotBorderEffect *border_effect; @@ -1932,7 +1924,7 @@ void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationA /** TextAnnotation [Annotation] */ -TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(0), inplaceIntent(TextAnnotation::Unknown) { } +TextAnnotationPrivate::TextAnnotationPrivate() : textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(0), inplaceIntent(TextAnnotation::Unknown) { } Annotation *TextAnnotationPrivate::makeAlias() { @@ -2078,7 +2070,7 @@ TextAnnotation::TextAnnotation(const QDomNode &node) : Annotation(*new TextAnnot } } -TextAnnotation::~TextAnnotation() { } +TextAnnotation::~TextAnnotation() = default; void TextAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -2417,7 +2409,7 @@ class LineAnnotationPrivate : public AnnotationPrivate }; LineAnnotationPrivate::LineAnnotationPrivate() - : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) + : lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) { } @@ -2527,7 +2519,7 @@ LineAnnotation::LineAnnotation(const QDomNode &node) : Annotation(*new LineAnnot } } -LineAnnotation::~LineAnnotation() { } +LineAnnotation::~LineAnnotation() = default; void LineAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -2664,9 +2656,8 @@ void LineAnnotation::setLinePoints(const QLinkedList &points) lineann->setVertices(x1, y1, x2, y2); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); - AnnotPath *p = d->toAnnotPath(points); - polyann->setVertices(p); - delete p; + const std::unique_ptr p = d->toAnnotPath(points); + polyann->setVertices(*p); } } @@ -2977,7 +2968,7 @@ class GeomAnnotationPrivate : public AnnotationPrivate QColor geomInnerColor; }; -GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { } +GeomAnnotationPrivate::GeomAnnotationPrivate() : geomType(GeomAnnotation::InscribedSquare) { } Annotation *GeomAnnotationPrivate::makeAlias() { @@ -3040,7 +3031,7 @@ GeomAnnotation::GeomAnnotation(const QDomNode &node) : Annotation(*new GeomAnnot } } -GeomAnnotation::~GeomAnnotation() { } +GeomAnnotation::~GeomAnnotation() = default; void GeomAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -3140,7 +3131,7 @@ class HighlightAnnotationPrivate : public AnnotationPrivate AnnotQuadrilaterals *toQuadrilaterals(const QList &quads) const; }; -HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { } +HighlightAnnotationPrivate::HighlightAnnotationPrivate() : highlightType(HighlightAnnotation::Highlight) { } Annotation *HighlightAnnotationPrivate::makeAlias() { @@ -3291,7 +3282,7 @@ HighlightAnnotation::HighlightAnnotation(const QDomNode &node) : Annotation(*new } } -HighlightAnnotation::~HighlightAnnotation() { } +HighlightAnnotation::~HighlightAnnotation() = default; void HighlightAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -3397,7 +3388,7 @@ void HighlightAnnotation::setHighlightQuads(const QList(d->pdfAnnot); AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads); - hlann->setQuadrilaterals(quadrilaterals); + hlann->setQuadrilaterals(*quadrilaterals); delete quadrilaterals; } @@ -3416,7 +3407,7 @@ class StampAnnotationPrivate : public AnnotationPrivate QImage stampCustomImage; }; -StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { } +StampAnnotationPrivate::StampAnnotationPrivate() : stampIconName(QStringLiteral("Draft")) { } Annotation *StampAnnotationPrivate::makeAlias() { @@ -3498,14 +3489,12 @@ AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHel case QImage::Format_Grayscale8: bitsPerComponent = 8; break; -#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) case QImage::Format_Grayscale16: convertedQImage = convertedQImage.convertToFormat(QImage::Format_Grayscale8); colorSpace = ColorSpace::DeviceGray; bitsPerComponent = 8; break; -#endif case QImage::Format_RGB16: case QImage::Format_RGB666: case QImage::Format_RGB555: @@ -3565,7 +3554,7 @@ StampAnnotation::StampAnnotation(const QDomNode &node) : Annotation(*new StampAn } } -StampAnnotation::~StampAnnotation() { } +StampAnnotation::~StampAnnotation() = default; void StampAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -3644,10 +3633,10 @@ class InkAnnotationPrivate : public AnnotationPrivate QList> inkPaths; // helper - AnnotPath **toAnnotPaths(const QList> &paths); + std::vector> toAnnotPaths(const QList> &paths); }; -InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { } +InkAnnotationPrivate::InkAnnotationPrivate() = default; Annotation *InkAnnotationPrivate::makeAlias() { @@ -3655,12 +3644,12 @@ Annotation *InkAnnotationPrivate::makeAlias() } // Note: Caller is required to delete array elements and the array itself after use -AnnotPath **InkAnnotationPrivate::toAnnotPaths(const QList> &paths) +std::vector> InkAnnotationPrivate::toAnnotPaths(const QList> &paths) { - const int pathsNumber = paths.size(); - AnnotPath **res = new AnnotPath *[pathsNumber]; - for (int i = 0; i < pathsNumber; ++i) { - res[i] = toAnnotPath(paths[i]); + std::vector> res; + res.reserve(paths.size()); + for (const auto &path : paths) { + res.push_back(toAnnotPath(path)); } return res; } @@ -3742,7 +3731,7 @@ InkAnnotation::InkAnnotation(const QDomNode &node) : Annotation(*new InkAnnotati } } -InkAnnotation::~InkAnnotation() { } +InkAnnotation::~InkAnnotation() = default; void InkAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -3789,21 +3778,19 @@ QList> InkAnnotation::inkPaths() const const AnnotInk *inkann = static_cast(d->pdfAnnot); - const AnnotPath *const *paths = inkann->getInkList(); - if (!paths || !inkann->getInkListLength()) { + const std::vector> &paths = inkann->getInkList(); + if (paths.empty()) { return QList>(); } double MTX[6]; d->fillTransformationMTX(MTX); - const int pathsNumber = inkann->getInkListLength(); QList> inkPaths; - inkPaths.reserve(pathsNumber); - for (int m = 0; m < pathsNumber; ++m) { + inkPaths.reserve(paths.size()); + for (const auto &path : paths) { // transform each path in a list of normalized points .. QLinkedList localList; - const AnnotPath *path = paths[m]; const int pointsNumber = path ? path->getCoordsLength() : 0; for (int n = 0; n < pointsNumber; ++n) { QPointF point; @@ -3826,14 +3813,8 @@ void InkAnnotation::setInkPaths(const QList> &paths) } AnnotInk *inkann = static_cast(d->pdfAnnot); - AnnotPath **annotpaths = d->toAnnotPaths(paths); - const int pathsNumber = paths.size(); - inkann->setInkList(annotpaths, pathsNumber); - - for (int i = 0; i < pathsNumber; ++i) { - delete annotpaths[i]; - } - delete[] annotpaths; + const std::vector> annotpaths = d->toAnnotPaths(paths); + inkann->setInkList(annotpaths); } /** LinkAnnotation [Annotation] */ @@ -3851,7 +3832,7 @@ class LinkAnnotationPrivate : public AnnotationPrivate QPointF linkRegion[4]; }; -LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkDestination(nullptr), linkHLMode(LinkAnnotation::Invert) { } +LinkAnnotationPrivate::LinkAnnotationPrivate() : linkDestination(nullptr), linkHLMode(LinkAnnotation::Invert) { } LinkAnnotationPrivate::~LinkAnnotationPrivate() { @@ -3956,7 +3937,7 @@ LinkAnnotation::LinkAnnotation(const QDomNode &node) : Annotation(*new LinkAnnot } } -LinkAnnotation::~LinkAnnotation() { } +LinkAnnotation::~LinkAnnotation() = default; void LinkAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4083,6 +4064,13 @@ void LinkAnnotation::store(QDomNode &node, QDomDocument &document) const hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("Hide")); break; } + case Poppler::Link::ResetForm: { + hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("ResetForm")); + break; + } + case Poppler::Link::SubmitForm: { + hyperlinkElement.setAttribute(QStringLiteral("type"), QStringLiteral("SubmitForm")); + } case Poppler::Link::None: break; } @@ -4172,7 +4160,7 @@ static CaretAnnotation::CaretSymbol caretSymbolFromString(const QString &symbol) return CaretAnnotation::None; } -CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { } +CaretAnnotationPrivate::CaretAnnotationPrivate() : symbol(CaretAnnotation::None) { } Annotation *CaretAnnotationPrivate::makeAlias() { @@ -4225,7 +4213,7 @@ CaretAnnotation::CaretAnnotation(const QDomNode &node) : Annotation(*new CaretAn } } -CaretAnnotation::~CaretAnnotation() { } +CaretAnnotation::~CaretAnnotation() = default; void CaretAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4286,7 +4274,7 @@ class FileAttachmentAnnotationPrivate : public AnnotationPrivate EmbeddedFile *embfile; }; -FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { } +FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : icon(QStringLiteral("PushPin")), embfile(nullptr) { } FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() { @@ -4323,7 +4311,7 @@ FileAttachmentAnnotation::FileAttachmentAnnotation(const QDomNode &node) : Annot } } -FileAttachmentAnnotation::~FileAttachmentAnnotation() { } +FileAttachmentAnnotation::~FileAttachmentAnnotation() = default; void FileAttachmentAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4378,7 +4366,7 @@ class SoundAnnotationPrivate : public AnnotationPrivate SoundObject *sound; }; -SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { } +SoundAnnotationPrivate::SoundAnnotationPrivate() : icon(QStringLiteral("Speaker")), sound(nullptr) { } SoundAnnotationPrivate::~SoundAnnotationPrivate() { @@ -4415,7 +4403,7 @@ SoundAnnotation::SoundAnnotation(const QDomNode &node) : Annotation(*new SoundAn } } -SoundAnnotation::~SoundAnnotation() { } +SoundAnnotation::~SoundAnnotation() = default; void SoundAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4470,7 +4458,7 @@ class MovieAnnotationPrivate : public AnnotationPrivate QString title; }; -MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { } +MovieAnnotationPrivate::MovieAnnotationPrivate() : movie(nullptr) { } MovieAnnotationPrivate::~MovieAnnotationPrivate() { @@ -4507,7 +4495,7 @@ MovieAnnotation::MovieAnnotation(const QDomNode &node) : Annotation(*new MovieAn } } -MovieAnnotation::~MovieAnnotation() { } +MovieAnnotation::~MovieAnnotation() = default; void MovieAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4562,7 +4550,7 @@ class ScreenAnnotationPrivate : public AnnotationPrivate QString title; }; -ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { } +ScreenAnnotationPrivate::ScreenAnnotationPrivate() : action(nullptr) { } ScreenAnnotationPrivate::~ScreenAnnotationPrivate() { @@ -4583,7 +4571,7 @@ Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { } -ScreenAnnotation::~ScreenAnnotation() { } +ScreenAnnotation::~ScreenAnnotation() = default; void ScreenAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4652,7 +4640,7 @@ WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { } -WidgetAnnotation::~WidgetAnnotation() { } +WidgetAnnotation::~WidgetAnnotation() = default; void WidgetAnnotation::store(QDomNode &node, QDomDocument &document) const { @@ -4679,14 +4667,14 @@ Link *WidgetAnnotation::additionalAction(AdditionalActionType type) const class RichMediaAnnotation::Params::Private { public: - Private() { } + Private() = default; QString flashVars; }; RichMediaAnnotation::Params::Params() : d(new Private) { } -RichMediaAnnotation::Params::~Params() { } +RichMediaAnnotation::Params::~Params() = default; void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars) { @@ -4714,7 +4702,7 @@ class RichMediaAnnotation::Instance::Private RichMediaAnnotation::Instance::Instance() : d(new Private) { } -RichMediaAnnotation::Instance::~Instance() { } +RichMediaAnnotation::Instance::~Instance() = default; void RichMediaAnnotation::Instance::setType(Type type) { @@ -4740,7 +4728,7 @@ RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const class RichMediaAnnotation::Configuration::Private { public: - Private() { } + Private() = default; ~Private() { qDeleteAll(instances); @@ -4757,7 +4745,7 @@ class RichMediaAnnotation::Configuration::Private RichMediaAnnotation::Configuration::Configuration() : d(new Private) { } -RichMediaAnnotation::Configuration::~Configuration() { } +RichMediaAnnotation::Configuration::~Configuration() = default; void RichMediaAnnotation::Configuration::setType(Type type) { @@ -4808,7 +4796,7 @@ class RichMediaAnnotation::Asset::Private RichMediaAnnotation::Asset::Asset() : d(new Private) { } -RichMediaAnnotation::Asset::~Asset() { } +RichMediaAnnotation::Asset::~Asset() = default; void RichMediaAnnotation::Asset::setName(const QString &name) { @@ -4834,7 +4822,7 @@ EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const class RichMediaAnnotation::Content::Private { public: - Private() { } + Private() = default; ~Private() { qDeleteAll(configurations); @@ -4853,7 +4841,7 @@ class RichMediaAnnotation::Content::Private RichMediaAnnotation::Content::Content() : d(new Private) { } -RichMediaAnnotation::Content::~Content() { } +RichMediaAnnotation::Content::~Content() = default; void RichMediaAnnotation::Content::setConfigurations(const QList &configurations) { @@ -4891,7 +4879,7 @@ class RichMediaAnnotation::Activation::Private RichMediaAnnotation::Activation::Activation() : d(new Private) { } -RichMediaAnnotation::Activation::~Activation() { } +RichMediaAnnotation::Activation::~Activation() = default; void RichMediaAnnotation::Activation::setCondition(Condition condition) { @@ -4913,7 +4901,7 @@ class RichMediaAnnotation::Deactivation::Private : public QSharedData RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { } -RichMediaAnnotation::Deactivation::~Deactivation() { } +RichMediaAnnotation::Deactivation::~Deactivation() = default; void RichMediaAnnotation::Deactivation::setCondition(Condition condition) { @@ -4936,7 +4924,7 @@ class RichMediaAnnotation::Settings::Private : public QSharedData RichMediaAnnotation::Settings::Settings() : d(new Private) { } -RichMediaAnnotation::Settings::~Settings() { } +RichMediaAnnotation::Settings::~Settings() = default; void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation) { @@ -5007,7 +4995,7 @@ RichMediaAnnotation::RichMediaAnnotation(const QDomNode &node) : Annotation(*new } } -RichMediaAnnotation::~RichMediaAnnotation() { } +RichMediaAnnotation::~RichMediaAnnotation() = default; void RichMediaAnnotation::store(QDomNode &node, QDomDocument &document) const { diff --git a/qt5/src/poppler-base-converter.cc b/qt5/src/poppler-base-converter.cc index 9533103..c5feb6b 100644 --- a/qt5/src/poppler-base-converter.cc +++ b/qt5/src/poppler-base-converter.cc @@ -27,7 +27,7 @@ namespace Poppler { BaseConverterPrivate::BaseConverterPrivate() : document(nullptr), iodev(nullptr), ownIodev(true) { } -BaseConverterPrivate::~BaseConverterPrivate() { } +BaseConverterPrivate::~BaseConverterPrivate() = default; QIODevice *BaseConverterPrivate::openDevice() { diff --git a/qt5/src/poppler-document.cc b/qt5/src/poppler-document.cc index 6daed69..959141f 100644 --- a/qt5/src/poppler-document.cc +++ b/qt5/src/poppler-document.cc @@ -1,7 +1,7 @@ /* poppler-document.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, 2008, Brad Hards - * Copyright (C) 2005-2010, 2012, 2013, 2015, 2017-2022, Albert Astals Cid + * Copyright (C) 2005-2010, 2012, 2013, 2015, 2017-2022, 2024, Albert Astals Cid * Copyright (C) 2006-2010, Pino Toscano * Copyright (C) 2010, 2011 Hib Eris * Copyright (C) 2012 Koji Otani @@ -20,6 +20,9 @@ * Copyright (C) 2020 Thorsten Behrens * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 Stefan Brüns + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,9 +56,11 @@ #include #include #include +#include #include "poppler-form.h" #include "poppler-private.h" +#include "poppler-link-private.h" #include "poppler-page-private.h" #include "poppler-outline-private.h" @@ -243,9 +248,8 @@ QByteArray Document::fontData(const FontInfo &fi) const Object refObj(fi.m_data->embRef); Object strObj = refObj.fetch(xref); - if (strObj.isStream()) { + if (strObj.isStream() && strObj.streamReset()) { int c; - strObj.streamReset(); while ((c = strObj.streamGetChar()) != EOF) { result.append((char)c); } @@ -272,8 +276,8 @@ bool Document::setInfo(const QString &key, const QString &val) return false; } - GooString *goo = QStringToUnicodeGooString(val); - m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); + std::unique_ptr goo = QStringToUnicodeGooString(val); + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), std::move(goo)); return true; } @@ -595,14 +599,12 @@ QDomDocument *Document::toc() const } const std::vector<::OutlineItem *> *items = outline->getItems(); - if (!items || items->size() < 1) { + if (!items || items->empty()) { return nullptr; } QDomDocument *toc = new QDomDocument(); - if (items->size() > 0) { - m_doc->addTocChildren(toc, toc, items); - } + m_doc->addTocChildren(toc, toc, items); return toc; } @@ -624,10 +626,9 @@ QVector Document::outline() const LinkDestination *Document::linkDestination(const QString &name) { - GooString *namedDest = QStringToGooString(name); - LinkDestinationData ldd(nullptr, namedDest, m_doc, false); + const std::unique_ptr namedDest = QStringToGooString(name); + LinkDestinationData ldd(nullptr, namedDest.get(), m_doc, false); LinkDestination *ld = new LinkDestination(ldd); - delete namedDest; return ld; } @@ -770,16 +771,64 @@ OptContentModel *Document::optionalContentModel() return (OptContentModel *)m_doc->m_optContentModel; } +Link *Document::additionalAction(DocumentAdditionalActionsType type) const +{ + Catalog::DocumentAdditionalActionsType actionType; + switch (type) { + case CloseDocument: + actionType = Catalog::actionCloseDocument; + break; + case SaveDocumentStart: + actionType = Catalog::actionSaveDocumentStart; + break; + case SaveDocumentFinish: + actionType = Catalog::actionSaveDocumentFinish; + break; + case PrintDocumentStart: + actionType = Catalog::actionPrintDocumentStart; + break; + case PrintDocumentFinish: + actionType = Catalog::actionPrintDocumentFinish; + break; + default: + return {}; + } + + Link *action = nullptr; + if (std::unique_ptr<::LinkAction> act = m_doc->doc->getCatalog()->getAdditionalAction(actionType)) { + action = PageData::convertLinkActionToLink(act.get(), m_doc, QRectF()); + } + + return action; +} + +void Document::applyResetFormsLink(const LinkResetForm &link) +{ + const LinkResetFormPrivate *lrfp = link.d_func(); + Catalog *catalog = m_doc->doc->getCatalog(); + if (catalog && catalog->isOk()) { + Form *form = catalog->getForm(); + if (form) { + std::vector stdStringFields; + const QStringList fields = lrfp->m_fields; + stdStringFields.reserve(fields.size()); + for (const auto &field : fields) { + stdStringFields.emplace_back(field.toStdString()); + } + form->reset(stdStringFields, lrfp->m_exclude); + } + } +} + QStringList Document::scripts() const { Catalog *catalog = m_doc->doc->getCatalog(); const int numScripts = catalog->numJS(); QStringList scripts; for (int i = 0; i < numScripts; ++i) { - GooString *s = catalog->getJS(i); - if (s) { + std::string s = catalog->getJS(i); + if (!s.empty()) { scripts.append(UnicodeParsedString(s)); - delete s; } } return scripts; @@ -872,22 +921,22 @@ QDateTime convertDate(const char *dateString) QDate d(year, mon, day); QTime t(hour, min, sec); if (d.isValid() && t.isValid()) { - QDateTime dt(d, t, Qt::UTC); + int tzSecs = 0; if (tz) { // then we have some form of timezone if ('Z' == tz) { // We are already at UTC } else if ('+' == tz) { // local time is ahead of UTC - dt = dt.addSecs(-1 * ((tzHours * 60) + tzMins) * 60); + tzSecs = (tzHours * 3600) + (tzMins * 60); } else if ('-' == tz) { // local time is behind UTC - dt = dt.addSecs(((tzHours * 60) + tzMins) * 60); + tzSecs = (tzHours * -3600) + (tzMins * -60); } else { qWarning("unexpected tz val"); } } - return dt; + return QDateTime(d, t, QTimeZone(tzSecs)); } } return QDateTime(); diff --git a/qt5/src/poppler-embeddedfile.cc b/qt5/src/poppler-embeddedfile.cc index bab8d73..cc69a10 100644 --- a/qt5/src/poppler-embeddedfile.cc +++ b/qt5/src/poppler-embeddedfile.cc @@ -3,6 +3,7 @@ * Copyright (C) 2005, Brad Hards * Copyright (C) 2008, 2011, Pino Toscano * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich + * Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,16 +104,11 @@ QByteArray EmbeddedFile::data() return QByteArray(); } - stream->reset(); - int dataLen = 0; - QByteArray fileArray; - int i; - while ((i = stream->getChar()) != EOF) { - fileArray[dataLen] = (char)i; - ++dataLen; + if (!stream->reset()) { + return QByteArray(); } - fileArray.resize(dataLen); - return fileArray; + auto data = stream->toUnsignedChars(); + return QByteArray(reinterpret_cast(data.data()), data.size()); } bool EmbeddedFile::isValid() const diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc index a411151..20523bf 100644 --- a/qt5/src/poppler-form.cc +++ b/qt5/src/poppler-form.cc @@ -1,6 +1,6 @@ /* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, 2011, Pino Toscano - * Copyright (C) 2008, 2011, 2012, 2015-2022 Albert Astals Cid + * Copyright (C) 2008, 2011, 2012, 2015-2024 Albert Astals Cid * Copyright (C) 2011 Carlos Garcia Campos * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow @@ -16,6 +16,9 @@ * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2021 Theofilos Intzoglou * Copyright (C) 2022 Alexander Sulfrian + * Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 Stefan Brüns * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +40,7 @@ #include #include +#include #include #include @@ -44,8 +48,12 @@ #include #include #include +#include #ifdef ENABLE_NSS3 -# include +# include +#endif +#ifdef ENABLE_GPGME +# include #endif #include "poppler-page-private.h" @@ -55,10 +63,6 @@ #include #include -#ifdef ENABLE_NSS3 -# include -#endif - namespace { Qt::Alignment formTextAlignment(::FormWidget *fm) @@ -159,15 +163,14 @@ QString FormField::name() const void FormField::setName(const QString &name) const { - GooString *goo = QStringToGooString(name); + const std::unique_ptr goo = QStringToGooString(name); m_formData->fm->setPartialName(*goo); - delete goo; } QString FormField::fullyQualifiedName() const { QString name; - if (GooString *goo = m_formData->fm->getFullyQualifiedName()) { + if (const GooString *goo = m_formData->fm->getFullyQualifiedName()) { name = UnicodeParsedString(goo); } return name; @@ -284,7 +287,7 @@ Link *FormField::additionalAction(Annotation::AdditionalActionType type) const FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldButton::~FormFieldButton() { } +FormFieldButton::~FormFieldButton() = default; FormFieldButton::FormType FormFieldButton::type() const { @@ -393,7 +396,7 @@ QList FormFieldButton::siblings() const FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldText::~FormFieldText() { } +FormFieldText::~FormFieldText() = default; FormField::FormType FormFieldText::type() const { @@ -420,17 +423,15 @@ QString FormFieldText::text() const void FormFieldText::setText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); - GooString *goo = QStringToUnicodeGooString(text); - fwt->setContent(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwt->setContent(std::move(goo)); } void FormFieldText::setAppearanceText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); - GooString *goo = QStringToUnicodeGooString(text); - fwt->setAppearanceContent(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwt->setAppearanceContent(std::move(goo)); } bool FormFieldText::isPassword() const @@ -477,7 +478,7 @@ void FormFieldText::setFontSize(int fontSize) FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldChoice::~FormFieldChoice() { } +FormFieldChoice::~FormFieldChoice() = default; FormFieldChoice::FormType FormFieldChoice::type() const { @@ -570,9 +571,8 @@ void FormFieldChoice::setEditChoice(const QString &text) FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo() && fwc->hasEdit()) { - GooString *goo = QStringToUnicodeGooString(text); - fwc->setEditChoice(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwc->setEditChoice(std::move(goo)); } } @@ -587,6 +587,13 @@ bool FormFieldChoice::canBeSpellChecked() const return !fwc->noSpellCheck(); } +void FormFieldChoice::setAppearanceChoiceText(const QString &text) +{ + FormWidgetChoice *fwc = static_cast(m_formData->fm); + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwc->setAppearanceChoiceContent(std::move(goo)); +} + class CertificateInfoPrivate { public: @@ -612,6 +619,8 @@ class CertificateInfoPrivate int version; bool is_self_signed; bool is_null; + CertificateInfo::KeyLocation keyLocation; + CertificateInfo::CertificateType certificateType; }; CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) @@ -621,7 +630,7 @@ CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { } -CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { } +CertificateInfo::CertificateInfo(const CertificateInfo &other) = default; CertificateInfo::~CertificateInfo() = default; @@ -652,6 +661,12 @@ QByteArray CertificateInfo::serialNumber() const return d->serial_number; } +CertificateInfo::CertificateType CertificateInfo::certificateType() const +{ + Q_D(const CertificateInfo); + return d->certificateType; +} + QString CertificateInfo::issuerInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); @@ -737,6 +752,12 @@ CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const return kuExtensions; } +CertificateInfo::KeyLocation CertificateInfo::keyLocation() const +{ + Q_D(const CertificateInfo); + return d->keyLocation; +} + QByteArray CertificateInfo::publicKey() const { Q_D(const CertificateInfo); @@ -778,14 +799,18 @@ QByteArray CertificateInfo::certificateData() const bool CertificateInfo::checkPassword(const QString &password) const { -#ifdef ENABLE_NSS3 +#ifdef ENABLE_SIGNATURES + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + return false; + } Q_D(const CertificateInfo); - SignatureHandler sigHandler(d->nick_name.toUtf8().constData(), SEC_OID_SHA256); + auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256); unsigned char buffer[5]; memcpy(buffer, "test", 5); - sigHandler.updateHash(buffer, 5); - std::unique_ptr tmpSignature = sigHandler.signDetached(password.toUtf8().constData()); - return tmpSignature.get() != nullptr; + sigHandler->addData(buffer, 5); + std::variant, CryptoSign::SigningError> tmpSignature = sigHandler->signDetached(password.toStdString()); + return std::holds_alternative>(tmpSignature); #else return false; #endif @@ -805,7 +830,7 @@ class SignatureValidationInfoPrivate QString signer_subject_dn; QString location; QString reason; - int hash_algorithm; + HashAlgorithm hash_algorithm; time_t signing_time; QList range_bounds; qint64 docLength; @@ -813,9 +838,9 @@ class SignatureValidationInfoPrivate SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { } -SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { } +SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) = default; -SignatureValidationInfo::~SignatureValidationInfo() { } +SignatureValidationInfo::~SignatureValidationInfo() = default; SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const { @@ -855,24 +880,26 @@ QString SignatureValidationInfo::reason() const SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const { -#ifdef ENABLE_NSS3 +#ifdef ENABLE_SIGNATURES Q_D(const SignatureValidationInfo); switch (d->hash_algorithm) { - case HASH_AlgMD2: + case ::HashAlgorithm::Md2: return HashAlgorithmMd2; - case HASH_AlgMD5: + case ::HashAlgorithm::Md5: return HashAlgorithmMd5; - case HASH_AlgSHA1: + case ::HashAlgorithm::Sha1: return HashAlgorithmSha1; - case HASH_AlgSHA256: + case ::HashAlgorithm::Sha256: return HashAlgorithmSha256; - case HASH_AlgSHA384: + case ::HashAlgorithm::Sha384: return HashAlgorithmSha384; - case HASH_AlgSHA512: + case ::HashAlgorithm::Sha512: return HashAlgorithmSha512; - case HASH_AlgSHA224: + case ::HashAlgorithm::Sha224: return HashAlgorithmSha224; + case ::HashAlgorithm::Unknown: + return HashAlgorithmUnknown; } #endif return HashAlgorithmUnknown; @@ -931,7 +958,7 @@ SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValid FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldSignature::~FormFieldSignature() { } +FormFieldSignature::~FormFieldSignature() = default; FormField::FormType FormFieldSignature::type() const { @@ -943,28 +970,59 @@ FormFieldSignature::SignatureType FormFieldSignature::signatureType() const SignatureType sigType = AdbePkcs7detached; FormWidgetSignature *fws = static_cast(m_formData->fm); switch (fws->signatureType()) { - case adbe_pkcs7_sha1: + case CryptoSign::SignatureType::adbe_pkcs7_sha1: sigType = AdbePkcs7sha1; break; - case adbe_pkcs7_detached: + case CryptoSign::SignatureType::adbe_pkcs7_detached: sigType = AdbePkcs7detached; break; - case ETSI_CAdES_detached: + case CryptoSign::SignatureType::ETSI_CAdES_detached: sigType = EtsiCAdESdetached; break; - case unknown_signature_type: + case CryptoSign::SignatureType::unknown_signature_type: sigType = UnknownSignatureType; break; - case unsigned_signature_field: + case CryptoSign::SignatureType::unsigned_signature_field: sigType = UnsignedSignature; break; + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + sigType = G10cPgpSignatureDetached; + break; } return sigType; } SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const { - return validate(opt, QDateTime()); + auto tempResult = validateAsync(opt); + tempResult.first.d_ptr->certificate_status = validateResult(); + return tempResult.first; +} + +static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location) +{ + switch (location) { + case KeyLocation::Computer: + return CertificateInfo::KeyLocation::Computer; + case KeyLocation::Other: + return CertificateInfo::KeyLocation::Other; + case KeyLocation::Unknown: + return CertificateInfo::KeyLocation::Unknown; + case KeyLocation::HardwareToken: + return CertificateInfo::KeyLocation::HardwareToken; + } + return CertificateInfo::KeyLocation::Unknown; +} + +static CertificateInfo::CertificateType fromPopplerCore(CertificateType type) +{ + switch (type) { + case CertificateType::PGP: + return CertificateInfo::CertificateType::PGP; + case CertificateType::X509: + return CertificateInfo::CertificateType::X509; + } + return CertificateInfo::CertificateType::X509; // fallback } static CertificateInfoPrivate *createCertificateInfoPrivate(const X509CertificateInfo *ci) @@ -974,6 +1032,8 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat if (ci) { certPriv->version = ci->getVersion(); certPriv->ku_extensions = ci->getKeyUsageExtensions(); + certPriv->keyLocation = fromPopplerCore(ci->getKeyLocation()); + certPriv->certificateType = fromPopplerCore(ci->getCertificateType()); const GooString &certSerial = ci->getSerialNumber(); certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength()); @@ -993,8 +1053,8 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat certPriv->nick_name = ci->getNickName().c_str(); X509CertificateInfo::Validity certValidity = ci->getValidity(); - certPriv->validity_start = QDateTime::fromSecsSinceEpoch(certValidity.notBefore, Qt::UTC); - certPriv->validity_end = QDateTime::fromSecsSinceEpoch(certValidity.notAfter, Qt::UTC); + certPriv->validity_start = QDateTime::fromSecsSinceEpoch(certValidity.notBefore, QTimeZone::utc()); + certPriv->validity_end = QDateTime::fromSecsSinceEpoch(certValidity.notAfter, QTimeZone::utc()); const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo(); certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength()); @@ -1010,12 +1070,29 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat return certPriv; } -SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const +static SignatureValidationInfo::CertificateStatus fromInternal(CertificateValidationStatus status) { - FormWidgetSignature *fws = static_cast(m_formData->fm); - const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1; - SignatureInfo *si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch); + switch (status) { + case CERTIFICATE_TRUSTED: + return SignatureValidationInfo::CertificateTrusted; + case CERTIFICATE_UNTRUSTED_ISSUER: + return SignatureValidationInfo::CertificateUntrustedIssuer; + case CERTIFICATE_UNKNOWN_ISSUER: + return SignatureValidationInfo::CertificateUnknownIssuer; + case CERTIFICATE_REVOKED: + return SignatureValidationInfo::CertificateRevoked; + case CERTIFICATE_EXPIRED: + return SignatureValidationInfo::CertificateExpired; + default: + case CERTIFICATE_GENERIC_ERROR: + return SignatureValidationInfo::CertificateGenericError; + case CERTIFICATE_NOT_VERIFIED: + return SignatureValidationInfo::CertificateNotVerified; + } +} +static SignatureValidationInfo fromInternal(SignatureInfo *si, FormWidgetSignature *fws) +{ // get certificate info const X509CertificateInfo *ci = si->getCertificateInfo(); CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci); @@ -1045,32 +1122,9 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v priv->signature_status = SignatureValidationInfo::SignatureNotVerified; break; } - switch (si->getCertificateValStatus()) { - case CERTIFICATE_TRUSTED: - priv->certificate_status = SignatureValidationInfo::CertificateTrusted; - break; - case CERTIFICATE_UNTRUSTED_ISSUER: - priv->certificate_status = SignatureValidationInfo::CertificateUntrustedIssuer; - break; - case CERTIFICATE_UNKNOWN_ISSUER: - priv->certificate_status = SignatureValidationInfo::CertificateUnknownIssuer; - break; - case CERTIFICATE_REVOKED: - priv->certificate_status = SignatureValidationInfo::CertificateRevoked; - break; - case CERTIFICATE_EXPIRED: - priv->certificate_status = SignatureValidationInfo::CertificateExpired; - break; - default: - case CERTIFICATE_GENERIC_ERROR: - priv->certificate_status = SignatureValidationInfo::CertificateGenericError; - break; - case CERTIFICATE_NOT_VERIFIED: - priv->certificate_status = SignatureValidationInfo::CertificateNotVerified; - break; - } - priv->signer_name = si->getSignerName(); - priv->signer_subject_dn = si->getSubjectDN(); + priv->certificate_status = SignatureValidationInfo::CertificateVerificationInProgress; + priv->signer_name = QString::fromStdString(si->getSignerName()); + priv->signer_subject_dn = QString::fromStdString(si->getSubjectDN()); priv->hash_algorithm = si->getHashAlgorithm(); priv->location = UnicodeParsedString(si->getLocation().toStr()); priv->reason = UnicodeParsedString(si->getReason().toStr()); @@ -1090,10 +1144,54 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v return SignatureValidationInfo(priv); } +SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const +{ + auto tempResult = validateAsync(static_cast(opt), validationTime); + tempResult.first.d_ptr->certificate_status = validateResult(); + return tempResult.first; +} + +class AsyncObjectPrivate +{ /*Currently unused. Created for abi future proofing*/ +}; + +AsyncObject::AsyncObject() : QObject(nullptr) { } + +AsyncObject::~AsyncObject() = default; + +std::pair> FormFieldSignature::validateAsync(ValidateOptions opt, const QDateTime &validationTime) const +{ + auto object = std::make_shared(); + FormWidgetSignature *fws = static_cast(m_formData->fm); + const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1; + SignatureInfo *si = fws->validateSignatureAsync(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch, + [obj = std::weak_ptr(object)]() { + if (auto l = obj.lock()) { + // We need to roundtrip over the eventloop + // to ensure callers have a chance of connecting to AsyncObject::done + QMetaObject::invokeMethod( + l.get(), + [innerObj = std::weak_ptr(l)]() { + if (auto innerLocked = innerObj.lock()) { + emit innerLocked->done(); + } + }, + Qt::QueuedConnection); + } + }); + + return { fromInternal(si, fws), object }; +} + +SignatureValidationInfo::CertificateStatus FormFieldSignature::validateResult() const +{ + return fromInternal(static_cast(m_formData->fm)->validateSignatureResult()); +} + FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const { FormWidgetSignature *fws = static_cast(m_formData->fm); - if (fws->signatureType() != unsigned_signature_field) { + if (fws->signatureType() != CryptoSign::SignatureType::unsigned_signature_field) { return FieldAlreadySigned; } @@ -1111,9 +1209,9 @@ FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &output const auto gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); const auto gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); - const bool success = - fws->signDocumentWithAppearance(outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), "SHA256", data.password().toUtf8().constData(), reason.get(), location.get(), ownerPwd, userPwd, *gSignatureText, - *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), convertQColor(data.borderColor()), convertQColor(data.backgroundColor())); + const bool success = !fws->signDocumentWithAppearance(outputFileName.toStdString(), data.certNickname().toStdString(), data.password().toStdString(), reason.get(), location.get(), ownerPwd, userPwd, *gSignatureText, *gSignatureLeftText, + data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), convertQColor(data.borderColor()), convertQColor(data.backgroundColor())) + .has_value(); return success ? SigningSuccess : GenericSigningError; } @@ -1129,24 +1227,113 @@ bool hasNSSSupport() QVector getAvailableSigningCertificates() { + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + return {}; + } QVector vReturnCerts; - -#ifdef ENABLE_NSS3 - std::vector> vCerts = SignatureHandler::getAvailableSigningCertificates(); + std::vector> vCerts = backend->getAvailableSigningCertificates(); for (auto &cert : vCerts) { CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get()); vReturnCerts.append(CertificateInfo(certPriv)); } -#endif return vReturnCerts; } +static std::optional convertToFrontend(std::optional type) +{ + if (!type) { + return std::nullopt; + } + switch (type.value()) { + case CryptoSign::Backend::Type::NSS3: + return CryptoSignBackend::NSS; + case CryptoSign::Backend::Type::GPGME: + return CryptoSignBackend::GPG; + } + return std::nullopt; +} + +static std::optional convertToBackend(std::optional backend) +{ + if (!backend) { + return std::nullopt; + } + switch (backend.value()) { + case CryptoSignBackend::NSS: + return CryptoSign::Backend::Type::NSS3; + case CryptoSignBackend::GPG: + return CryptoSign::Backend::Type::GPGME; + } + return std::nullopt; +} + +QVector availableCryptoSignBackends() +{ + QVector backends; + for (auto &backend : CryptoSign::Factory::getAvailable()) { + auto converted = convertToFrontend(backend); + if (converted) { + backends.push_back(converted.value()); + } + } + return backends; +} + +std::optional activeCryptoSignBackend() +{ + return convertToFrontend(CryptoSign::Factory::getActive()); +} + +bool setActiveCryptoSignBackend(CryptoSignBackend backend) +{ + auto available = availableCryptoSignBackends(); + if (!available.contains(backend)) { + return false; + } + auto converted = convertToBackend(backend); + if (!converted) { + return false; + } + CryptoSign::Factory::setPreferredBackend(converted.value()); + return activeCryptoSignBackend() == backend; +} + +static bool hasNSSBackendFeature(CryptoSignBackendFeature feature) +{ + switch (feature) { + case CryptoSignBackendFeature::BackendAsksPassphrase: + return false; + } + return false; +} + +static bool hasGPGBackendFeature(CryptoSignBackendFeature feature) +{ + switch (feature) { + case CryptoSignBackendFeature::BackendAsksPassphrase: + return true; + } + return false; +} + +bool hasCryptoSignBackendFeature(CryptoSignBackend backend, CryptoSignBackendFeature feature) +{ + switch (backend) { + case CryptoSignBackend::NSS: + return hasNSSBackendFeature(feature); + case CryptoSignBackend::GPG: + return hasGPGBackendFeature(feature); + } + return false; +} + QString POPPLER_QT5_EXPORT getNSSDir() { #ifdef ENABLE_NSS3 - return QString::fromLocal8Bit(SignatureHandler::getNSSDir().c_str()); + return QString::fromLocal8Bit(NSSSignatureConfiguration::getNSSDir().c_str()); #else return QString(); #endif @@ -1159,9 +1346,8 @@ void setNSSDir(const QString &path) return; } - GooString *goo = QStringToGooString(path); - SignatureHandler::setNSSDir(*goo); - delete goo; + const std::unique_ptr goo = QStringToGooString(path); + NSSSignatureConfiguration::setNSSDir(*goo); #else (void)path; #endif @@ -1174,11 +1360,29 @@ std::function nssPasswordCall; void setNSSPasswordCallback(const std::function &f) { #ifdef ENABLE_NSS3 - SignatureHandler::setNSSPasswordCallback(f); + NSSSignatureConfiguration::setNSSPasswordCallback(f); #else qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support"; (void)f; #endif } +void setPgpSignaturesAllowed(bool allowed) +{ +#ifdef ENABLE_GPGME + GpgSignatureConfiguration::setPgpSignaturesAllowed(allowed); +#else + qWarning() << "Trying to enable pgp signatures, but pgp not enabled in this build"; + (void)allowed; +#endif +} + +bool arePgpSignaturesAllowed() +{ +#ifdef ENABLE_GPGME + return GpgSignatureConfiguration::arePgpSignaturesAllowed(); +#else + return false; +#endif +} } diff --git a/qt5/src/poppler-form.h b/qt5/src/poppler-form.h index 0e64f2e..60169c8 100644 --- a/qt5/src/poppler-form.h +++ b/qt5/src/poppler-form.h @@ -13,6 +13,8 @@ * Copyright (C) 2020, Thorsten Behrens * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Theofilos Intzoglou + * Copyright (C) 2023, 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela + * Copyright (C) 2024, Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -481,6 +484,14 @@ class POPPLER_QT5_EXPORT FormFieldChoice : public FormField */ bool canBeSpellChecked() const; + /** + Sets the text inside the Appearance Stream to the specified + \p text + + \since 24.08 + */ + void setAppearanceChoiceText(const QString &text); + private: Q_DISABLE_COPY(FormFieldChoice) }; @@ -522,6 +533,17 @@ class POPPLER_QT5_EXPORT CertificateInfo }; Q_DECLARE_FLAGS(KeyUsageExtensions, KeyUsageExtension) + /** + * The type of the certificate + * + * \since 25.03 + */ + enum class CertificateType + { + X509, + PGP + }; + /** Predefined keys for elements in an entity's distinguished name. */ @@ -533,6 +555,24 @@ class POPPLER_QT5_EXPORT CertificateInfo Organization, }; + /** A signing key can be located in different places + sometimes. For the user, it might be easier to pick + the key located on a card if it has some visual + indicator that it is somehow removable. + + \note a keylocation for a certificate without a private + key (cannot be used for signing) will likely be "Unknown" + + \since 23.09 + */ + enum class KeyLocation + { + Unknown, /** We don't know the location */ + Other, /** We know the location, but it is somehow not covered by this enum */ + Computer, /** The key is on this computer */ + HardwareToken /** The key is on a dedicated hardware token, either a smartcard or a dedicated usb token (e.g. gnuk, nitrokey or yubikey) */ + }; + CertificateInfo(); explicit CertificateInfo(CertificateInfoPrivate *priv); ~CertificateInfo(); @@ -604,6 +644,13 @@ class POPPLER_QT5_EXPORT CertificateInfo */ bool isSelfSigned() const; + /** + * \return certificate type + * + * \since 25.03 + */ + CertificateType certificateType() const; + /** The DER encoded certificate. */ @@ -616,6 +663,13 @@ class POPPLER_QT5_EXPORT CertificateInfo */ bool checkPassword(const QString &password) const; + /** + The storage location for this key + + \since 23.09 + */ + KeyLocation keyLocation() const; + CertificateInfo(const CertificateInfo &other); CertificateInfo &operator=(const CertificateInfo &other); @@ -660,7 +714,8 @@ class POPPLER_QT5_EXPORT SignatureValidationInfo CertificateRevoked, ///< The certificate was revoked by the issuing certificate authority. CertificateExpired, ///< The signing time is outside the validity bounds of this certificate. CertificateGenericError, ///< The certificate could not be verified. - CertificateNotVerified ///< The certificate is not yet verified. + CertificateNotVerified, ///< The certificate is not yet verified. + CertificateVerificationInProgress ///< The certificate is not yet verified but is in progress in the background. See \ref validateAsync \since 24.05 }; /** @@ -758,10 +813,32 @@ class POPPLER_QT5_EXPORT SignatureValidationInfo private: Q_DECLARE_PRIVATE(SignatureValidationInfo) - + friend class FormFieldSignature; QSharedPointer d_ptr; }; +/** + * Object help waiting for some async event + * + * \since 24.05 + */ +class AsyncObjectPrivate; +class POPPLER_QT5_EXPORT AsyncObject : public QObject // clazy:exclude=ctor-missing-parent-argument +{ + Q_OBJECT +public: + /* Constructor. On purpose not having a QObject parameter + It will be returned by shared_ptr or unique_ptr + */ + AsyncObject(); + ~AsyncObject() override; +Q_SIGNALS: + void done(); + +private: + std::unique_ptr d; +}; + /** A form field that represents a signature. @@ -780,7 +857,8 @@ class POPPLER_QT5_EXPORT FormFieldSignature : public FormField AdbePkcs7detached, EtsiCAdESdetached, UnknownSignatureType, ///< \since 0.90 - UnsignedSignature ///< \since 22.02 + UnsignedSignature, ///< \since 22.02 + G10cPgpSignatureDetached, ///< non standard signature type \since 25.01 }; /** @@ -811,8 +889,16 @@ class POPPLER_QT5_EXPORT FormFieldSignature : public FormField Validate the signature with now as validation time. Reset signature validatation info of scoped instance. + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background */ - SignatureValidationInfo validate(ValidateOptions opt) const; + POPPLER_QT5_DEPRECATED SignatureValidationInfo validate(ValidateOptions opt) const; /** Validate the signature with @p validationTime as validation time. @@ -820,8 +906,44 @@ class POPPLER_QT5_EXPORT FormFieldSignature : public FormField Reset signature validatation info of scoped instance. \since 0.58 + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background */ - SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; + POPPLER_QT5_DEPRECATED SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; + + /** + Validate the signature with @p validationTime as validation time. + + Reset signature validatation info of scoped instance. + + \since 24.05 + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \note certificate validation will have started when this function return. See \ref validateResult on how to get certifcate validation + \note connections to \ref AsyncObject must happen by the caller + before returning control to the event loop, else signals is not guaranteed to be delivered + */ + std::pair> validateAsync(ValidateOptions opt, const QDateTime &validationTime = {}) const; + + /** + * \return the updated signature validation info from validateAsync + * \note that this function will block if the result is not yet ready. + * Wait for the \ref AsyncObject::done signal to avoid this function blocking on an inconvenient time + * + * \since 24.05 + */ + SignatureValidationInfo::CertificateStatus validateResult() const; /** * \since 22.02 @@ -846,12 +968,64 @@ class POPPLER_QT5_EXPORT FormFieldSignature : public FormField Q_DISABLE_COPY(FormFieldSignature) }; +/** + * Possible compiled in backends for signature handling + * + * \since 23.06 + */ +enum class CryptoSignBackend +{ + NSS, + GPG +}; + +/** + * The available compiled-in backends + * + * \since 23.06 + */ +QVector POPPLER_QT5_EXPORT availableCryptoSignBackends(); + +/** + * Returns current active backend or nullopt if none is active + * + * \note there will always be an active backend if there is available backends + * + * \since 23.06 + */ +std::optional POPPLER_QT5_EXPORT activeCryptoSignBackend(); + +/** + * Sets active backend + * + * \return true on success + * + * \since 23.06 + */ +bool POPPLER_QT5_EXPORT setActiveCryptoSignBackend(CryptoSignBackend backend); + +enum class CryptoSignBackendFeature +{ + /// If the backend itself out of band requests passwords + /// or if the host applicaion somehow must do it + BackendAsksPassphrase +}; + +/** + * Queries if a backend supports or not supports a given feature. + * + * \since 23.06 + */ +bool POPPLER_QT5_EXPORT hasCryptoSignBackendFeature(CryptoSignBackend, CryptoSignBackendFeature); + /** Returns is poppler was compiled with NSS support + \deprecated Use availableBackends instead + \since 21.01 */ -bool POPPLER_QT5_EXPORT hasNSSSupport(); +bool POPPLER_QT5_DEPRECATED POPPLER_QT5_EXPORT hasNSSSupport(); /** Return vector of suitable signing certificates @@ -880,6 +1054,23 @@ void POPPLER_QT5_EXPORT setNSSDir(const QString &pathURL); \since 21.01 */ void POPPLER_QT5_EXPORT setNSSPasswordCallback(const std::function &f); + +/** + * Allow pgp signatures in pdf files (standard-extension, experimental) + * Not supported for NSS backend + * + * \param allowed new value for pgp signatures allowed + * + * \since 25.03 + */ +void POPPLER_QT5_EXPORT setPgpSignaturesAllowed(bool allowed); + +/** + * \returns if pgp signatures are allowed. + * + * \since 25.03 + */ +bool POPPLER_QT5_EXPORT arePgpSignaturesAllowed(); } #endif diff --git a/qt5/src/poppler-link-private.h b/qt5/src/poppler-link-private.h index 7e6f6b0..772f66f 100644 --- a/qt5/src/poppler-link-private.h +++ b/qt5/src/poppler-link-private.h @@ -2,6 +2,7 @@ * Copyright (C) 2016, 2018, 2020, 2021 Albert Astals Cid * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ #include +#include "poppler-link.h" #include "Link.h" class LinkOCGState; @@ -67,6 +69,27 @@ class LinkHidePrivate : public LinkPrivate bool isShow; }; +class LinkResetFormPrivate : public LinkPrivate +{ +public: + LinkResetFormPrivate(const QRectF &area, const QStringList &fields, const bool exclude) : LinkPrivate(area), m_fields(fields), m_exclude(exclude) { } + ~LinkResetFormPrivate() override; + + QStringList m_fields; + bool m_exclude; +}; + +class LinkSubmitFormPrivate : public LinkPrivate +{ +public: + LinkSubmitFormPrivate(const QRectF &area, const QVector &fieldIds, const QString &url, LinkSubmitForm::SubmitFormFlags flags) : LinkPrivate(area), m_fieldIds(fieldIds), m_url(url), m_flags(flags) { } + ~LinkSubmitFormPrivate() override; + + QVector m_fieldIds; + QString m_url; + LinkSubmitForm::SubmitFormFlags m_flags; +}; + } #endif diff --git a/qt5/src/poppler-link.cc b/qt5/src/poppler-link.cc index cb114ea..685f26e 100644 --- a/qt5/src/poppler-link.cc +++ b/qt5/src/poppler-link.cc @@ -1,5 +1,5 @@ /* poppler-link.cc: qt interface to poppler - * Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid + * Copyright (C) 2006-2007, 2013, 2016-2021, 2024, Albert Astals Cid * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2012, Tobias Koenig @@ -7,6 +7,7 @@ * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2018 Adam Reichold * Copyright (C) 2020 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -78,6 +79,10 @@ LinkOCGStatePrivate::~LinkOCGStatePrivate() = default; LinkHidePrivate::~LinkHidePrivate() = default; +LinkResetFormPrivate::~LinkResetFormPrivate() = default; + +LinkSubmitFormPrivate::~LinkSubmitFormPrivate() = default; + class LinkGotoPrivate : public LinkPrivate { public: @@ -151,17 +156,17 @@ LinkSoundPrivate::~LinkSoundPrivate() class LinkRenditionPrivate : public LinkPrivate { public: - explicit LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); + explicit LinkRenditionPrivate(const QRectF &area, std::unique_ptr<::MediaRendition> &&rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); ~LinkRenditionPrivate() override; - MediaRendition *rendition; + std::unique_ptr rendition; LinkRendition::RenditionAction action; QString script; Ref annotationReference; }; -LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref) - : LinkPrivate(area), rendition(r ? new MediaRendition(r) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref) +LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, std::unique_ptr<::MediaRendition> &&r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref) + : LinkPrivate(area), rendition(r ? new MediaRendition(std::move(r)) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref) { switch (operation) { case ::LinkRendition::NoRendition: @@ -182,10 +187,7 @@ LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition } } -LinkRenditionPrivate::~LinkRenditionPrivate() -{ - delete rendition; -} +LinkRenditionPrivate::~LinkRenditionPrivate() = default; class LinkJavaScriptPrivate : public LinkPrivate { @@ -313,9 +315,9 @@ LinkDestination::LinkDestination(const QString &description) : d(new LinkDestina } } -LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { } +LinkDestination::LinkDestination(const LinkDestination &other) = default; -LinkDestination::~LinkDestination() { } +LinkDestination::~LinkDestination() = default; LinkDestination::Kind LinkDestination::kind() const { @@ -370,15 +372,15 @@ bool LinkDestination::isChangeZoom() const QString LinkDestination::toString() const { QString s = QString::number((qint8)d->kind); - s += ";" + QString::number(d->pageNum); - s += ";" + QString::number(d->left); - s += ";" + QString::number(d->bottom); - s += ";" + QString::number(d->right); - s += ";" + QString::number(d->top); - s += ";" + QString::number(d->zoom); - s += ";" + QString::number((qint8)d->changeLeft); - s += ";" + QString::number((qint8)d->changeTop); - s += ";" + QString::number((qint8)d->changeZoom); + s += ';' + QString::number(d->pageNum); + s += ';' + QString::number(d->left); + s += ';' + QString::number(d->bottom); + s += ';' + QString::number(d->right); + s += ';' + QString::number(d->top); + s += ';' + QString::number(d->zoom); + s += ';' + QString::number((qint8)d->changeLeft); + s += ';' + QString::number((qint8)d->changeTop); + s += ';' + QString::number((qint8)d->changeZoom); return s; } @@ -431,7 +433,7 @@ LinkGoto::LinkGoto(const QRectF &linkArea, QString extFileName, const LinkDestin d->extFileName = std::move(extFileName); // TODO remove when extFileName moves to be a const & } -LinkGoto::~LinkGoto() { } +LinkGoto::~LinkGoto() = default; bool LinkGoto::isExternal() const { @@ -464,7 +466,7 @@ LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QStr d->parameters = params; } -LinkExecute::~LinkExecute() { } +LinkExecute::~LinkExecute() = default; QString LinkExecute::fileName() const { @@ -489,7 +491,7 @@ LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new L d->url = url; } -LinkBrowse::~LinkBrowse() { } +LinkBrowse::~LinkBrowse() = default; QString LinkBrowse::url() const { @@ -509,7 +511,7 @@ LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*ne d->type = actionType; } -LinkAction::~LinkAction() { } +LinkAction::~LinkAction() = default; LinkAction::ActionType LinkAction::actionType() const { @@ -533,7 +535,7 @@ LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repe d->sound = sound; } -LinkSound::~LinkSound() { } +LinkSound::~LinkSound() = default; Link::LinkType LinkSound::linkType() const { @@ -572,11 +574,16 @@ SoundObject *LinkSound::sound() const // LinkRendition LinkRendition::LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference) // clazy:exclude=function-args-by-value - : Link(*new LinkRenditionPrivate(linkArea, rendition, static_cast(operation), script, annotationReference)) + : LinkRendition(linkArea, std::unique_ptr<::MediaRendition>(rendition), operation, script, annotationReference) +{ +} + +LinkRendition::LinkRendition(const QRectF &linkArea, std::unique_ptr<::MediaRendition> &&rendition, int operation, const QString &script, const Ref annotationReference) + : Link(*new LinkRenditionPrivate(linkArea, std::move(rendition), static_cast(operation), script, annotationReference)) { } -LinkRendition::~LinkRendition() { } +LinkRendition::~LinkRendition() = default; Link::LinkType LinkRendition::linkType() const { @@ -586,7 +593,7 @@ Link::LinkType LinkRendition::linkType() const MediaRendition *LinkRendition::rendition() const { Q_D(const LinkRendition); - return d->rendition; + return d->rendition.get(); } LinkRendition::RenditionAction LinkRendition::action() const @@ -618,7 +625,7 @@ LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link d->js = js; } -LinkJavaScript::~LinkJavaScript() { } +LinkJavaScript::~LinkJavaScript() = default; Link::LinkType LinkJavaScript::linkType() const { @@ -637,7 +644,7 @@ LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString { } -LinkMovie::~LinkMovie() { } +LinkMovie::~LinkMovie() = default; Link::LinkType LinkMovie::linkType() const { @@ -664,7 +671,7 @@ bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { } -LinkOCGState::~LinkOCGState() { } +LinkOCGState::~LinkOCGState() = default; Link::LinkType LinkOCGState::linkType() const { @@ -674,7 +681,7 @@ Link::LinkType LinkOCGState::linkType() const // LinkHide LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { } -LinkHide::~LinkHide() { } +LinkHide::~LinkHide() = default; Link::LinkType LinkHide::linkType() const { @@ -692,4 +699,58 @@ bool LinkHide::isShowAction() const Q_D(const LinkHide); return d->isShow; } + +// LinkResetForm +LinkResetForm::LinkResetForm(LinkResetFormPrivate *lrfp) : Link(*lrfp) { } + +LinkResetForm::~LinkResetForm() = default; + +Link::LinkType LinkResetForm::linkType() const +{ + return ResetForm; +} + +// LinkSubmitForm +// static assertions to ensure flags from Link.h match those from poppler-link.h +static_assert(static_cast(LinkSubmitForm::NoOpFlag) == static_cast(::LinkSubmitForm::NoOpFlag), "NoOpFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExcludeFlag) == static_cast(::LinkSubmitForm::ExcludeFlag), "ExcludeFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeNoValueFieldsFlag) == static_cast(::LinkSubmitForm::IncludeNoValueFieldsFlag), "IncludeNoValueFieldsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExportFormatFlag) == static_cast(::LinkSubmitForm::ExportFormatFlag), "ExportFormatFlag does not match"); +static_assert(static_cast(LinkSubmitForm::GetMethodFlag) == static_cast(::LinkSubmitForm::GetMethodFlag), "GetMethodFlag does not match"); +static_assert(static_cast(LinkSubmitForm::SubmitCoordinatesFlag) == static_cast(::LinkSubmitForm::SubmitCoordinatesFlag), "SubmitCoordinatesFlag does not match"); +static_assert(static_cast(LinkSubmitForm::XFDFFlag) == static_cast(::LinkSubmitForm::XFDFFlag), "XFDFFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeAppendSavesFlag) == static_cast(::LinkSubmitForm::IncludeAppendSavesFlag), "IncludeAppendSavesFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeAnnotationsFlag) == static_cast(::LinkSubmitForm::IncludeAnnotationsFlag), "IncludeAnnotationsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::SubmitPDFFlag) == static_cast(::LinkSubmitForm::SubmitPDFFlag), "SubmitPDFFlag does not match"); +static_assert(static_cast(LinkSubmitForm::CanonicalFormatFlag) == static_cast(::LinkSubmitForm::CanonicalFormatFlag), "CanonicalFormatFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExclNonUserAnnotsFlag) == static_cast(::LinkSubmitForm::ExclNonUserAnnotsFlag), "ExclNonUserAnnotsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExclFKeyFlag) == static_cast(::LinkSubmitForm::ExclFKeyFlag), "ExclFKeyFlag does not match"); +static_assert(static_cast(LinkSubmitForm::EmbedFormFlag) == static_cast(::LinkSubmitForm::EmbedFormFlag), "EmbedFormFlag does not match"); + +LinkSubmitForm::LinkSubmitForm(LinkSubmitFormPrivate *lsfp) : Link(*lsfp) { } + +LinkSubmitForm::~LinkSubmitForm() = default; + +Link::LinkType LinkSubmitForm::linkType() const +{ + return SubmitForm; +} + +QVector LinkSubmitForm::getFieldIds() const +{ + Q_D(const LinkSubmitForm); + return d->m_fieldIds; +} + +QString LinkSubmitForm::getUrl() const +{ + Q_D(const LinkSubmitForm); + return d->m_url; +} + +LinkSubmitForm::SubmitFormFlags LinkSubmitForm::getFlags() const +{ + Q_D(const LinkSubmitForm); + return d->m_flags; +} } diff --git a/qt5/src/poppler-link.h b/qt5/src/poppler-link.h index 5de3542..c165226 100644 --- a/qt5/src/poppler-link.h +++ b/qt5/src/poppler-link.h @@ -1,11 +1,12 @@ /* poppler-link.h: qt interface to poppler - * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, Albert Astals Cid + * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, 2024, Albert Astals Cid * Copyright (C) 2007-2008, 2010, Pino Toscano * Copyright (C) 2010, 2012, Guillermo Amaral * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2013, Anthony Granger * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -33,6 +34,8 @@ #include #include "poppler-export.h" +#include + struct Ref; class MediaRendition; @@ -51,6 +54,8 @@ class LinkDestinationPrivate; class LinkRenditionPrivate; class LinkOCGStatePrivate; class LinkHidePrivate; +class LinkResetFormPrivate; +class LinkSubmitFormPrivate; class MediaRendition; class MovieAnnotation; class ScreenAnnotation; @@ -201,6 +206,8 @@ class POPPLER_QT5_EXPORT Link JavaScript, ///< A JavaScript code to be interpreted \since 0.10 OCGState, ///< An Optional Content Group state change \since 0.50 Hide, ///< An action to hide a field \since 0.64 + ResetForm, ///< An action to reset the form \since 24.07 + SubmitForm, ///< An action to submit a form \since 24.10 }; /** @@ -499,7 +506,18 @@ class POPPLER_QT5_EXPORT LinkRendition : public Link * \since 0.22 */ // TODO Next ABI break, remove & from annotationReference - LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference); + [[deprecated]] LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref &annotationReference); + + /** + * Create a new rendition link. + * + * \param linkArea the active area of the link + * \param rendition the media rendition object. + * \param operation the numeric operation (action) (@see ::LinkRendition::RenditionOperation) + * \param script the java script code + * \param annotationReference the object reference of the screen annotation associated with this rendition action + */ + LinkRendition(const QRectF &linkArea, std::unique_ptr<::MediaRendition> &&rendition, int operation, const QString &script, const Ref annotationReference); /** * Destructor. @@ -681,6 +699,90 @@ class POPPLER_QT5_EXPORT LinkHide : public Link Q_DISABLE_COPY(LinkHide) }; +/** + * ResetForm: an action to reset form fields. + * + * \since 24.07 + */ +class POPPLER_QT5_EXPORT LinkResetForm : public Link +{ + friend class Document; + +public: + /** + * Creates a new ResetForm link. This is only used by Poppler::Page + */ + explicit LinkResetForm(LinkResetFormPrivate *lrfp); + /* + * Destructor + */ + ~LinkResetForm() override; + + LinkType linkType() const override; + +private: + Q_DECLARE_PRIVATE(LinkResetForm) + Q_DISABLE_COPY(LinkResetForm) +}; + +/** + * SubmitForm : An action to submit a form. + * + * \since 24.10 + */ +class POPPLER_QT5_EXPORT LinkSubmitForm : public Link +{ +public: + enum SubmitFormFlag + { + NoOpFlag = 0, + ExcludeFlag = 1, + IncludeNoValueFieldsFlag = 1 << 1, + ExportFormatFlag = 1 << 2, + GetMethodFlag = 1 << 3, + SubmitCoordinatesFlag = 1 << 4, + XFDFFlag = 1 << 5, + IncludeAppendSavesFlag = 1 << 6, + IncludeAnnotationsFlag = 1 << 7, + SubmitPDFFlag = 1 << 8, + CanonicalFormatFlag = 1 << 9, + ExclNonUserAnnotsFlag = 1 << 10, + ExclFKeyFlag = 1 << 11, + // 13th high bit flag is undefined + EmbedFormFlag = 1 << 13, + }; + Q_DECLARE_FLAGS(SubmitFormFlags, SubmitFormFlag) + + /** + * Create a new SubmitFormLink. + */ + explicit LinkSubmitForm(LinkSubmitFormPrivate *lsfp); + /** + * Destructor + */ + ~LinkSubmitForm() override; + + LinkType linkType() const override; + + /** + * The ids of fields that are to be submitted. + */ + QVector getFieldIds() const; + + /** + * The url to which the form is to be submitted. + */ + QString getUrl() const; + + /** + * The flags specifying how the form should be submitted. + */ + SubmitFormFlags getFlags() const; + +private: + Q_DECLARE_PRIVATE(LinkSubmitForm) + Q_DISABLE_COPY(LinkSubmitForm) +}; } #endif diff --git a/qt5/src/poppler-media.cc b/qt5/src/poppler-media.cc index cf634ca..a61c177 100644 --- a/qt5/src/poppler-media.cc +++ b/qt5/src/poppler-media.cc @@ -1,6 +1,7 @@ /* poppler-media.cc: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. - * Copyright (C) 2013, 2018, 2021 Albert Astals Cid + * Copyright (C) 2013, 2018, 2021, 2024 Albert Astals Cid + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,17 +33,19 @@ namespace Poppler { class MediaRenditionPrivate { public: - explicit MediaRenditionPrivate(::MediaRendition *renditionA) : rendition(renditionA) { } + explicit MediaRenditionPrivate(std::unique_ptr<::MediaRendition> &&renditionA) : rendition(std::move(renditionA)) { } - ~MediaRenditionPrivate() { delete rendition; } + ~MediaRenditionPrivate() = default; MediaRenditionPrivate(const MediaRenditionPrivate &) = delete; MediaRenditionPrivate &operator=(const MediaRenditionPrivate &) = delete; - ::MediaRendition *rendition; + std::unique_ptr<::MediaRendition> rendition; }; -MediaRendition::MediaRendition(::MediaRendition *rendition) : d_ptr(new MediaRenditionPrivate(rendition)) { } +MediaRendition::MediaRendition(::MediaRendition *rendition) : MediaRendition(std::unique_ptr<::MediaRendition>(rendition)) { } + +MediaRendition::MediaRendition(std::unique_ptr<::MediaRendition> &&rendition) : d_ptr(new MediaRenditionPrivate(std::move(rendition))) { } MediaRendition::~MediaRendition() { @@ -86,12 +89,13 @@ QByteArray MediaRendition::data() const return QByteArray(); } + if (!s->reset()) { + return QByteArray(); + } QBuffer buffer; unsigned char data[BUFFER_MAX]; int bread; - buffer.open(QIODevice::WriteOnly); - s->reset(); while ((bread = s->doGetChars(BUFFER_MAX, data)) != 0) { buffer.write(reinterpret_cast(data), bread); } diff --git a/qt5/src/poppler-media.h b/qt5/src/poppler-media.h index 7d5fe93..cc76383 100644 --- a/qt5/src/poppler-media.h +++ b/qt5/src/poppler-media.h @@ -1,6 +1,6 @@ /* poppler-media.h: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. - * Copyright (C) 2012, 2013, 2021 Albert Astals Cid + * Copyright (C) 2012, 2013, 2021, 2024 Albert Astals Cid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ #include #include +#include + class MediaRendition; class QIODevice; @@ -42,7 +44,13 @@ class POPPLER_QT5_EXPORT MediaRendition /** Constructs a MediaRendition. Takes ownership of the passed rendition */ - explicit MediaRendition(::MediaRendition *rendition); + [[deprecated]] explicit MediaRendition(::MediaRendition *rendition); + + /** + Constructs a MediaRendition. + */ + explicit MediaRendition(std::unique_ptr<::MediaRendition> &&rendition); + ~MediaRendition(); /** diff --git a/qt5/src/poppler-optcontent-private.h b/qt5/src/poppler-optcontent-private.h index 589fb0b..0b1046d 100644 --- a/qt5/src/poppler-optcontent-private.h +++ b/qt5/src/poppler-optcontent-private.h @@ -4,6 +4,7 @@ * Copyright (C) 2008, Pino Toscano * Copyright (C) 2016, 2018, 2019, 2021, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -97,13 +98,13 @@ class OptContentItem class OptContentModelPrivate { public: - OptContentModelPrivate(OptContentModel *qq, OCGs *optContent); + OptContentModelPrivate(OptContentModel *qq, const OCGs *optContent); ~OptContentModelPrivate(); OptContentModelPrivate(const OptContentModelPrivate &) = delete; OptContentModelPrivate &operator=(const OptContentModelPrivate &) = delete; - void parseRBGroupsArray(Array *rBGroupArray); + void parseRBGroupsArray(const Array *rBGroupArray); OptContentItem *nodeFromIndex(const QModelIndex &index, bool canBeNull = false) const; QModelIndex indexFromItem(OptContentItem *node, int column) const; @@ -126,7 +127,7 @@ class OptContentModelPrivate private: void addChild(OptContentItem *parent, OptContentItem *child); - void parseOrderArray(OptContentItem *parentNode, Array *orderArray); + void parseOrderArray(OptContentItem *parentNode, const Array *orderArray); }; } diff --git a/qt5/src/poppler-optcontent.cc b/qt5/src/poppler-optcontent.cc index e2864bc..91b3e2d 100644 --- a/qt5/src/poppler-optcontent.cc +++ b/qt5/src/poppler-optcontent.cc @@ -8,6 +8,7 @@ * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019, 2020 Oliver Sander + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,7 +56,7 @@ RadioButtonGroup::RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarr } } -RadioButtonGroup::~RadioButtonGroup() { } +RadioButtonGroup::~RadioButtonGroup() = default; QSet RadioButtonGroup::setItemOn(OptContentItem *itemToSetOn) { @@ -96,7 +97,7 @@ OptContentItem::OptContentItem(const QString &label) OptContentItem::OptContentItem() : m_parent(nullptr), m_enabled(true) { } -OptContentItem::~OptContentItem() { } +OptContentItem::~OptContentItem() = default; void OptContentItem::appendRBGroup(RadioButtonGroup *rbgroup) { @@ -152,7 +153,7 @@ QSet OptContentItem::recurseListChildren(bool includeMe) const return ret; } -OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, OCGs *optContent) : q(qq) +OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, const OCGs *optContent) : q(qq) { m_rootNode = new OptContentItem(); const auto &ocgs = optContent->getOCGs(); @@ -184,7 +185,7 @@ OptContentModelPrivate::~OptContentModelPrivate() delete m_rootNode; } -void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array *orderArray) +void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, const Array *orderArray) { OptContentItem *lastItem = parentNode; for (int i = 0; i < orderArray->getLength(); ++i) { @@ -215,7 +216,7 @@ void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array * } } -void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) +void OptContentModelPrivate::parseRBGroupsArray(const Array *rBGroupArray) { if (!rBGroupArray) { return; @@ -233,7 +234,7 @@ void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) } } -OptContentModel::OptContentModel(OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) +OptContentModel::OptContentModel(const OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) { d = new OptContentModelPrivate(this, optContent); } @@ -350,7 +351,7 @@ bool OptContentModel::setData(const QModelIndex &index, const QVariant &value, i Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } - std::stable_sort(indexes.begin(), indexes.end()); + std::stable_sort(indexes.begin(), indexes.end()); // NOLINT(modernize-use-ranges) QModelIndexList doesn't support ranges requirements Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } @@ -411,7 +412,7 @@ void OptContentModel::applyLink(LinkOCGState *link) Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } - std::stable_sort(indexes.begin(), indexes.end()); + std::stable_sort(indexes.begin(), indexes.end()); // NOLINT(modernize-use-ranges) Qt5 QModelIndexList doesn't support ranges requirements Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } diff --git a/qt5/src/poppler-optcontent.h b/qt5/src/poppler-optcontent.h index a126140..0173037 100644 --- a/qt5/src/poppler-optcontent.h +++ b/qt5/src/poppler-optcontent.h @@ -4,6 +4,7 @@ * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013, Anthony Granger * Copyright (C) 2016, 2021, Albert Astals Cid + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -74,7 +75,7 @@ class POPPLER_QT5_EXPORT OptContentModel : public QAbstractItemModel void applyLink(LinkOCGState *link); private: - explicit OptContentModel(OCGs *optContent, QObject *parent = nullptr); + explicit OptContentModel(const OCGs *optContent, QObject *parent = nullptr); friend class OptContentModelPrivate; OptContentModelPrivate *d; diff --git a/qt5/src/poppler-outline.cc b/qt5/src/poppler-outline.cc index 5ff7e37..5cc20d6 100644 --- a/qt5/src/poppler-outline.cc +++ b/qt5/src/poppler-outline.cc @@ -3,6 +3,7 @@ * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019 Oliver Sander * Copyright (C) 2019 Albert Astals Cid + * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,7 +79,7 @@ QString OutlineItem::name() const if (name.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { - name = unicodeToQString(data->getTitle(), data->getTitleLength()); + name = unicodeToQString(data->getTitle()); } } diff --git a/qt5/src/poppler-page.cc b/qt5/src/poppler-page.cc index b2cea93..1424901 100644 --- a/qt5/src/poppler-page.cc +++ b/qt5/src/poppler-page.cc @@ -1,7 +1,7 @@ /* poppler-page.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Brad Hards - * Copyright (C) 2005-2022, Albert Astals Cid + * Copyright (C) 2005-2022, 2024, Albert Astals Cid * Copyright (C) 2005, Stefan Kebekus * Copyright (C) 2006-2011, Pino Toscano * Copyright (C) 2008 Carlos Garcia Campos @@ -27,6 +27,10 @@ * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Thomas Huxhorn + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines + * Copyright (C) 2024 Stefan Brüns + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -129,10 +133,6 @@ class Qt5SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelpe { SplashBitmap *b = getBitmap(); - const int bw = b->getWidth(); - const int bh = b->getHeight(); - const int brs = b->getRowSize(); - // If we use DeviceN8, convert to XBGR8. // If requested, also transfer Splash's internal alpha channel. const SplashBitmap::ConversionMode mode = ignorePaperColor ? SplashBitmap::conversionAlphaPremultiplied : SplashBitmap::conversionOpaque; @@ -140,6 +140,10 @@ class Qt5SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelpe const QImage::Format format = ignorePaperColor ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; if (b->convertToXBGR(mode)) { + const int bw = b->getWidth(); + const int bh = b->getHeight(); + const int brs = b->getRowSize(); + SplashColorPtr data = takeImageData ? b->takeData() : b->getDataPtr(); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { @@ -226,7 +230,7 @@ Link *PageData::convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc case actionLaunch: { LinkLaunch *e = (LinkLaunch *)a; const GooString *p = e->getParams(); - popplerLink = new LinkExecute(linkArea, e->getFileName()->c_str(), p ? p->c_str() : nullptr); + popplerLink = new LinkExecute(linkArea, UnicodeParsedString(e->getFileName()), p ? p->c_str() : nullptr); } break; case actionNamed: { @@ -316,7 +320,7 @@ Link *PageData::convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc reference = lrn->getScreenAnnot(); } - popplerLink = new LinkRendition(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : nullptr, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); + popplerLink = new LinkRendition(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : std::unique_ptr<::MediaRendition> {}, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); } break; case actionOCGState: { @@ -333,9 +337,39 @@ Link *PageData::convertLinkActionToLink(::LinkAction *a, DocumentData *parentDoc popplerLink = new LinkHide(lhp); } break; - case actionResetForm: - // Not handled in Qt5 front-end yet - break; + case actionResetForm: { + ::LinkResetForm *lrf = (::LinkResetForm *)a; + std::vector stdStringFields = lrf->getFields(); + QStringList qStringFields; + for (const std::string &str : stdStringFields) { + qStringFields << QString::fromStdString(str); + } + LinkResetFormPrivate *lrfp = new LinkResetFormPrivate(linkArea, qStringFields, lrf->getExclude()); + popplerLink = new LinkResetForm(lrfp); + } break; + case actionSubmitForm: { + ::LinkSubmitForm *lsf = (::LinkSubmitForm *)a; + const std::vector &stdStringFields = lsf->getFields(); + QVector fieldIds; + fieldIds.reserve(stdStringFields.size()); + Form *form = parentDoc->doc->getCatalog()->getForm(); + for (const std::string &fieldStr : stdStringFields) { + ::FormField *field = form->findFieldByFullyQualifiedNameOrRef(fieldStr); + if (field && !field->getNoExport()) { + int numWidgets = field->getNumWidgets(); + for (int i = 0; i < numWidgets; i++) { + ::FormWidget *widget = field->getWidget(i); + if (widget) { + fieldIds.append(field->getWidget(i)->getID()); + } + } + } + } + QString qStringUrl = QString::fromStdString(lsf->getUrl()); + LinkSubmitForm::SubmitFormFlags qFlags = static_cast(lsf->getFlags()); + LinkSubmitFormPrivate *lsfp = new LinkSubmitFormPrivate(linkArea, fieldIds, qStringUrl, qFlags); + popplerLink = new LinkSubmitForm(lsfp); + } break; case actionUnknown: break; @@ -645,7 +679,7 @@ QImage Page::thumbnail() const QString Page::text(const QRectF &r, TextLayout textLayout) const { TextOutputDev *output_dev; - GooString *s; + GooString s; QString result; const bool rawOrder = textLayout == RawOrderLayout; @@ -653,15 +687,18 @@ QString Page::text(const QRectF &r, TextLayout textLayout) const m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72, 0, false, true, false, -1, -1, -1, -1, nullptr, nullptr, nullptr, nullptr, true); if (r.isNull()) { const PDFRectangle *rect = m_page->page->getCropBox(); - s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2); + if (orientation() == Orientation::Portrait || orientation() == Orientation::UpsideDown) { + s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2); + } else { + s = output_dev->getText(rect->y1, rect->x1, rect->y2, rect->x2); + } } else { s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom()); } - result = QString::fromUtf8(s->c_str()); + result = QString::fromStdString(s.toStr()); delete output_dev; - delete s; return result; } diff --git a/qt5/src/poppler-pdf-converter.cc b/qt5/src/poppler-pdf-converter.cc index fa833a3..9524ff1 100644 --- a/qt5/src/poppler-pdf-converter.cc +++ b/qt5/src/poppler-pdf-converter.cc @@ -1,6 +1,6 @@ /* poppler-pdf-converter.cc: qt interface to poppler * Copyright (C) 2008, Pino Toscano - * Copyright (C) 2008, 2009, 2020-2022, Albert Astals Cid + * Copyright (C) 2008, 2009, 2020-2022, 2024, Albert Astals Cid * Copyright (C) 2020, Thorsten Behrens * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Klarälvdalens Datakonsult AB, a KDAB Group company, . @@ -8,6 +8,7 @@ * Copyright (C) 2021, Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022, Martin * Copyright (C) 2022, Felix Jung + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,7 +51,7 @@ class PDFConverterPrivate : public BaseConverterPrivate PDFConverter::PDFOptions opts; }; -PDFConverterPrivate::PDFConverterPrivate() : BaseConverterPrivate(), opts(nullptr) { } +PDFConverterPrivate::PDFConverterPrivate() : opts(nullptr) { } PDFConverterPrivate::~PDFConverterPrivate() = default; @@ -60,7 +61,7 @@ PDFConverter::PDFConverter(DocumentData *document) : BaseConverter(*new PDFConve d->document = document; } -PDFConverter::~PDFConverter() { } +PDFConverter::~PDFConverter() = default; void PDFConverter::setPDFOptions(PDFConverter::PDFOptions options) { @@ -140,9 +141,10 @@ bool PDFConverter::sign(const NewSignatureData &data) const auto location = std::unique_ptr(data.location().isEmpty() ? nullptr : QStringToUnicodeGooString(data.location())); const auto ownerPwd = std::optional(data.documentOwnerPassword().constData()); const auto userPwd = std::optional(data.documentUserPassword().constData()); - return doc->sign(d->outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), data.password().toUtf8().constData(), QStringToGooString(data.fieldPartialName()), data.page() + 1, - boundaryToPdfRectangle(destPage, data.boundingRectangle(), Annotation::FixedRotation), *gSignatureText, *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), - convertQColor(data.borderColor()), convertQColor(data.backgroundColor()), reason.get(), location.get(), data.imagePath().toStdString(), ownerPwd, userPwd); + return !doc->sign(d->outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), data.password().toUtf8().constData(), QStringToGooString(data.fieldPartialName()), data.page() + 1, + boundaryToPdfRectangle(destPage, data.boundingRectangle(), Annotation::FixedRotation), *gSignatureText, *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), + convertQColor(data.borderColor()), convertQColor(data.backgroundColor()), reason.get(), location.get(), data.imagePath().toStdString(), ownerPwd, userPwd) + .has_value(); } struct PDFConverter::NewSignatureData::NewSignatureDataPrivate diff --git a/qt5/src/poppler-private.cc b/qt5/src/poppler-private.cc index 228d8e8..c26dd35 100644 --- a/qt5/src/poppler-private.cc +++ b/qt5/src/poppler-private.cc @@ -1,16 +1,18 @@ /* poppler-private.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. - * Copyright (C) 2006, 2011, 2015, 2017-2020 by Albert Astals Cid + * Copyright (C) 2006, 2011, 2015, 2017-2020, 2024 by Albert Astals Cid * Copyright (C) 2008, 2010, 2011, 2014 by Pino Toscano * Copyright (C) 2013 by Thomas Freitag * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2018-2020 Adam Reichold - * Copyright (C) 2019, 2020 Oliver Sander + * Copyright (C) 2019, 2020, 2024 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, * Copyright (C) 2021 Mahmoud Khalil + * Copyright (C) 2023 Shivodit Gill + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -41,6 +43,16 @@ #include #include #include +#include + +#ifdef ANDROID +# include +# include +# include +# include +# include +# include +#endif namespace Poppler { @@ -94,6 +106,11 @@ QString unicodeToQString(const Unicode *u, int len) return QString::fromUtf8(convertedStr.c_str(), convertedStr.getLength()); } +QString unicodeToQString(const std::vector &u) +{ + return unicodeToQString(u.data(), u.size()); +} + QString UnicodeParsedString(const GooString *s1) { return (s1) ? UnicodeParsedString(s1->toStr()) : QString(); @@ -105,48 +122,45 @@ QString UnicodeParsedString(const std::string &s1) return QString(); } - if (GooString::hasUnicodeMarker(s1) || GooString::hasUnicodeMarkerLE(s1)) { + if (hasUnicodeByteOrderMark(s1) || hasUnicodeByteOrderMarkLE(s1)) { return QString::fromUtf16(reinterpret_cast(s1.c_str()), s1.size() / 2); } else { - int stringLength; - const char *cString = pdfDocEncodingToUTF16(s1, &stringLength); - auto result = QString::fromUtf16(reinterpret_cast(cString), stringLength / 2); - delete[] cString; + std::string cString = pdfDocEncodingToUTF16(s1); + auto result = QString::fromUtf16(reinterpret_cast(cString.c_str()), cString.size() / 2); return result; } } -GooString *QStringToUnicodeGooString(const QString &s) +std::unique_ptr QStringToUnicodeGooString(const QString &s) { if (s.isEmpty()) { - return new GooString(); + return std::make_unique(); } int len = s.length() * 2 + 2; - char *cstring = (char *)gmallocn(len, sizeof(char)); - cstring[0] = (char)0xfe; - cstring[1] = (char)0xff; - for (int i = 0; i < s.length(); ++i) { - cstring[2 + i * 2] = s.at(i).row(); - cstring[3 + i * 2] = s.at(i).cell(); + std::string string; + string.reserve(len); + string.push_back((char)0xfe); + string.push_back((char)0xff); + for (auto element : s) { + string.push_back(element.row()); + string.push_back(element.cell()); } - GooString *ret = new GooString(cstring, len); - gfree(cstring); - return ret; + return std::make_unique(std::move(string)); } -GooString *QStringToGooString(const QString &s) +std::unique_ptr QStringToGooString(const QString &s) { int len = s.length(); char *cstring = (char *)gmallocn(s.length(), sizeof(char)); for (int i = 0; i < len; ++i) { cstring[i] = s.at(i).unicode(); } - GooString *ret = new GooString(cstring, len); + std::unique_ptr ret = std::make_unique(cstring, len); gfree(cstring); return ret; } -GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) +std::unique_ptr QDateTimeToUnicodeGooString(const QDateTime &dt) { if (!dt.isValid()) { return nullptr; @@ -258,6 +272,29 @@ void DocumentData::init() m_optContentModel = nullptr; xrefReconstructed = false; xrefReconstructedCallback = {}; + +#ifdef ANDROID + // Copy fonts from android apk to the app's storage dir, and + // set the font directory path + QString assetsFontDir = QStringLiteral("assets:/share/fonts"); + QString fontsdir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/fonts"); + QDir fontPath = QDir(fontsdir); + + if (fontPath.mkpath(fontPath.absolutePath())) { + GlobalParams::setFontDir(fontPath.absolutePath().toStdString()); + QDirIterator iterator(assetsFontDir, QDir::NoFilter, QDirIterator::Subdirectories); + + while (iterator.hasNext()) { + iterator.next(); + QFileInfo fontFileInfo = iterator.fileInfo(); + QString fontFilePath = assetsFontDir + QStringLiteral("/") + fontFileInfo.fileName(); + QString destPath = fontPath.absolutePath() + QStringLiteral("/") + fontFileInfo.fileName(); + QFile::copy(fontFilePath, destPath); + } + } else { + GlobalParams::setFontDir(""); + } +#endif } void DocumentData::addTocChildren(QDomDocument *docSyn, QDomNode *parent, const std::vector<::OutlineItem *> *items) @@ -266,13 +303,7 @@ void DocumentData::addTocChildren(QDomDocument *docSyn, QDomNode *parent, const // iterate over every object in 'items' // 1. create element using outlineItem's title as tagName - QString name; - const Unicode *uniChar = outlineItem->getTitle(); - int titleLength = outlineItem->getTitleLength(); - name = unicodeToQString(uniChar, titleLength); - if (name.isEmpty()) { - continue; - } + QString name = unicodeToQString(outlineItem->getTitle()); QDomElement item = docSyn->createElement(name); parent->appendChild(item); @@ -312,5 +343,4 @@ FormFieldIconData *FormFieldIconData::getData(const FormFieldIcon &f) { return f.d_ptr; } - } diff --git a/qt5/src/poppler-private.h b/qt5/src/poppler-private.h index f94e654..3444233 100644 --- a/qt5/src/poppler-private.h +++ b/qt5/src/poppler-private.h @@ -1,7 +1,7 @@ /* poppler-private.h: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, 2008, Brad Hards - * Copyright (C) 2006-2009, 2011, 2012, 2017-2022 by Albert Astals Cid + * Copyright (C) 2006-2009, 2011, 2012, 2017-2022, 2024, 2025 by Albert Astals Cid * Copyright (C) 2007-2009, 2011, 2014 by Pino Toscano * Copyright (C) 2011 Andreas Hartmetz * Copyright (C) 2011 Hib Eris @@ -21,6 +21,7 @@ * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -73,6 +74,7 @@ namespace Poppler { /* borrowed from kpdf */ POPPLER_QT5_EXPORT QString unicodeToQString(const Unicode *u, int len); +POPPLER_QT5_EXPORT QString unicodeToQString(const std::vector &u); POPPLER_QT5_EXPORT QString UnicodeParsedString(const GooString *s1); @@ -80,11 +82,11 @@ POPPLER_QT5_EXPORT QString UnicodeParsedString(const std::string &s1); // Returns a big endian UTF-16 string with BOM or an empty string without BOM. // The caller owns the returned pointer. -POPPLER_QT5_EXPORT GooString *QStringToUnicodeGooString(const QString &s); +POPPLER_QT5_EXPORT std::unique_ptr QStringToUnicodeGooString(const QString &s); -POPPLER_QT5_EXPORT GooString *QStringToGooString(const QString &s); +POPPLER_QT5_EXPORT std::unique_ptr QStringToGooString(const QString &s); -GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); +std::unique_ptr QDateTimeToUnicodeGooString(const QDateTime &dt); void qt5ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg); @@ -111,9 +113,9 @@ class DocumentData : private GlobalParamsIniter m_filePath = filePath; #ifdef _WIN32 - doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #else - doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #endif } @@ -122,7 +124,7 @@ class DocumentData : private GlobalParamsIniter m_device = device; QIODeviceInStream *str = new QIODeviceInStream(device, 0, false, device->size(), Object(objNull)); init(); - doc = new PDFDoc(str, ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(str, ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); } DocumentData(const QByteArray &data, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt5ErrorFunction) @@ -131,7 +133,7 @@ class DocumentData : private GlobalParamsIniter fileContents = data; MemStream *str = new MemStream((char *)fileContents.data(), 0, fileContents.length(), Object(objNull)); init(); - doc = new PDFDoc(str, ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(str, ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); } void init(); @@ -143,10 +145,7 @@ class DocumentData : private GlobalParamsIniter void addTocChildren(QDomDocument *docSyn, QDomNode *parent, const std::vector<::OutlineItem *> *items); - void setPaperColor(const QColor &color) - { - paperColor = color; - } + void setPaperColor(const QColor &color) { paperColor = color; } void fillMembers() { @@ -200,14 +199,17 @@ class FontInfoData explicit FontInfoData(::FontInfo *fi) { - if (fi->getName()) { - fontName = fi->getName()->c_str(); + const std::optional &fiName = fi->getName(); + if (fiName) { + fontName = fiName->c_str(); } - if (fi->getFile()) { - fontFile = fi->getFile()->c_str(); + const std::optional &fiFile = fi->getFile(); + if (fiFile) { + fontFile = fiFile->c_str(); } - if (fi->getSubstituteName()) { - fontSubstituteName = fi->getSubstituteName()->c_str(); + const std::optional &fiSubstituteName = fi->getSubstituteName(); + if (fiSubstituteName) { + fontSubstituteName = fiSubstituteName->c_str(); } isEmbedded = fi->getEmbedded(); isSubset = fi->getSubset(); @@ -232,7 +234,7 @@ class FontIteratorData public: FontIteratorData(int startPage, DocumentData *dd) : fontInfoScanner(dd->doc, startPage), totalPages(dd->doc->getNumPages()), currentPage(qMax(startPage, 0) - 1) { } - ~FontIteratorData() { } + ~FontIteratorData() = default; FontInfoScanner fontInfoScanner; int totalPages; diff --git a/qt5/src/poppler-ps-converter.cc b/qt5/src/poppler-ps-converter.cc index fd2bbc1..d514056 100644 --- a/qt5/src/poppler-ps-converter.cc +++ b/qt5/src/poppler-ps-converter.cc @@ -7,6 +7,7 @@ * Copyright (C) 2013 Thomas Freitag * Copyright (C) 2014 Adrian Johnson * Copyright (C) 2020 William Bader + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,19 +61,7 @@ class PSConverterPrivate : public BaseConverterPrivate }; PSConverterPrivate::PSConverterPrivate() - : BaseConverterPrivate(), - hDPI(72), - vDPI(72), - rotate(0), - paperWidth(-1), - paperHeight(-1), - marginRight(0), - marginBottom(0), - marginLeft(0), - marginTop(0), - opts(PSConverter::Printing), - pageConvertedCallback(nullptr), - pageConvertedPayload(nullptr) + : hDPI(72), vDPI(72), rotate(0), paperWidth(-1), paperHeight(-1), marginRight(0), marginBottom(0), marginLeft(0), marginTop(0), opts(PSConverter::Printing), pageConvertedCallback(nullptr), pageConvertedPayload(nullptr) { } @@ -84,7 +73,7 @@ PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverte d->document = document; } -PSConverter::~PSConverter() { } +PSConverter::~PSConverter() = default; void PSConverter::setPageList(const QList &pageList) { @@ -162,6 +151,16 @@ void PSConverter::setStrictMargins(bool strictMargins) } } +void PSConverter::setForceOverprintPreview(bool forceOverprintPreview) +{ + Q_D(PSConverter); + if (forceOverprintPreview) { + d->opts |= ForceOverprintPreview; + } else { + d->opts &= ~ForceOverprintPreview; + } +} + void PSConverter::setForceRasterize(bool forceRasterize) { Q_D(PSConverter); @@ -235,6 +234,10 @@ bool PSConverter::convert() PSOutputDev *psOut = new PSOutputDev(outputToQIODevice, dev, pstitlechar, d->document->doc, pages, (d->opts & PrintToEPS) ? psModeEPS : psModePS, d->paperWidth, d->paperHeight, false, false, d->marginLeft, d->marginBottom, d->paperWidth - d->marginRight, d->paperHeight - d->marginTop, (d->opts & ForceRasterization) ? psAlwaysRasterize : psRasterizeWhenNeeded); + if (d->opts & ForceOverprintPreview) { + psOut->setForceRasterize(psAlwaysRasterize); + psOut->setOverprintPreview(true); + } if (d->opts & StrictMargins) { double xScale = ((double)d->paperWidth - (double)d->marginLeft - (double)d->marginRight) / (double)d->paperWidth; diff --git a/qt5/src/poppler-qiodeviceoutstream-private.h b/qt5/src/poppler-qiodeviceoutstream-private.h index 78172cd..616f0b6 100644 --- a/qt5/src/poppler-qiodeviceoutstream-private.h +++ b/qt5/src/poppler-qiodeviceoutstream-private.h @@ -3,6 +3,7 @@ * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2021, Albert Astals Cid * Copyright (C) 2021, Even Rouault + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,6 +39,7 @@ class QIODeviceOutStream : public OutStream void close() override; Goffset getPos() override; void put(char c) override; + size_t write(std::span data) override; void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: diff --git a/qt5/src/poppler-qiodeviceoutstream.cc b/qt5/src/poppler-qiodeviceoutstream.cc index 38ac769..73e3f08 100644 --- a/qt5/src/poppler-qiodeviceoutstream.cc +++ b/qt5/src/poppler-qiodeviceoutstream.cc @@ -3,6 +3,7 @@ * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2020, 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +30,7 @@ namespace Poppler { QIODeviceOutStream::QIODeviceOutStream(QIODevice *device) : m_device(device) { } -QIODeviceOutStream::~QIODeviceOutStream() { } +QIODeviceOutStream::~QIODeviceOutStream() = default; void QIODeviceOutStream::close() { } @@ -43,6 +44,11 @@ void QIODeviceOutStream::put(char c) m_device->putChar(c); } +size_t QIODeviceOutStream::write(std::span data) +{ + return m_device->write(reinterpret_cast(data.data()), data.size()); +} + static int poppler_vasprintf(char **buf_ptr, const char *format, va_list ap) GCC_PRINTF_FORMAT(2, 0); static int poppler_vasprintf(char **buf_ptr, const char *format, va_list ap) diff --git a/qt5/src/poppler-qt5.h b/qt5/src/poppler-qt5.h index abeb4fd..de8adad 100644 --- a/qt5/src/poppler-qt5.h +++ b/qt5/src/poppler-qt5.h @@ -29,6 +29,8 @@ * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022 Martin + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines + * Copyright (C) 2024 Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1868,6 +1870,13 @@ QString subject = m_doc->info("Subject"); */ OptContentModel *optionalContentModel(); + /** + Resets the form with the details contained in the \p link. + + \since 24.07 + */ + void applyResetFormsLink(const LinkResetForm &link); + /** Document-level JavaScript scripts. @@ -1878,6 +1887,31 @@ QString subject = m_doc->info("Subject"); */ QStringList scripts() const; + /** + Describes the flags for additional document actions i.e. + for executing document scripts at different events. + This flag is used by additionalAction method to return the + particular Link. + + \since 24.07 + */ + enum DocumentAdditionalActionsType + { + CloseDocument, ///< Performed before closing the document + SaveDocumentStart, ///< Performed before saving the document + SaveDocumentFinish, ///< Performed after saving the document + PrintDocumentStart, ///< Performed before printing the document + PrintDocumentFinish, ///< Performed after printing the document + }; + + /** + Returns the additional action of the given @p type for the document or + @c 0 if no action has been defined. + + \since 24.07 + */ + Link *additionalAction(DocumentAdditionalActionsType type) const; + /** The PDF identifiers. @@ -2038,7 +2072,8 @@ class POPPLER_QT5_EXPORT PSConverter : public BaseConverter StrictMargins = 0x00000002, ForceRasterization = 0x00000004, PrintToEPS = 0x00000008, ///< Output EPS instead of PS \since 0.20 - HideAnnotations = 0x00000010 ///< Don't print annotations \since 0.20 + HideAnnotations = 0x00000010, ///< Don't print annotations \since 0.20 + ForceOverprintPreview = 0x00000020 ///< Force rasterized overprint preview during conversion \since 23.09 }; Q_DECLARE_FLAGS(PSOptions, PSOption) @@ -2109,6 +2144,16 @@ class POPPLER_QT5_EXPORT PSConverter : public BaseConverter */ void setStrictMargins(bool strictMargins); + /** + Defines if the page will be rasterized to an image with overprint + preview enabled before printing. + + Defaults to false + + \since 23.09 + */ + void setForceOverprintPreview(bool forceOverprintPreview); + /** Defines if the page will be rasterized to an image before printing. Defaults to false */ void setForceRasterize(bool forceRasterize); diff --git a/qt5/src/poppler-sound.cc b/qt5/src/poppler-sound.cc index 4339ab9..46290e7 100644 --- a/qt5/src/poppler-sound.cc +++ b/qt5/src/poppler-sound.cc @@ -1,6 +1,7 @@ /* poppler-sound.cc: qt interface to poppler * Copyright (C) 2006-2007, Pino Toscano - * Copyright (C) 2008, 2018, 2020, Albert Astals Cid + * Copyright (C) 2008, 2018, 2020, 2024, Albert Astals Cid + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,7 +82,9 @@ QByteArray SoundObject::data() const } Stream *stream = m_soundData->m_soundObj->getStream(); - stream->reset(); + if (!stream->reset()) { + return QByteArray {}; + } int dataLen = 0; QByteArray fileArray; int i; diff --git a/qt5/tests/CMakeLists.txt b/qt5/tests/CMakeLists.txt index baadd42..e610413 100644 --- a/qt5/tests/CMakeLists.txt +++ b/qt5/tests/CMakeLists.txt @@ -17,7 +17,7 @@ macro(QT5_ADD_SIMPLETEST exe source) endmacro(QT5_ADD_SIMPLETEST) macro(QT5_ADD_QTEST exe source) - if (Qt5Test_FOUND) + if (Qt5Test_FOUND) string(REPLACE "-" "" test_name ${exe}) set(${test_name}_SOURCES ${source} @@ -70,6 +70,11 @@ qt5_add_qtest(check_qt5_object check_object.cpp) qt5_add_qtest(check_qt5_stroke_opacity check_stroke_opacity.cpp) qt5_add_qtest(check_qt5_utf_conversion check_utf_conversion.cpp) qt5_add_qtest(check_qt5_outline check_outline.cpp) +qt5_add_qtest(check_qt5_signature_basics check_signature_basics.cpp) +qt5_add_qtest(check_qt5_utf8document check_utf8document.cpp) +qt5_add_qtest(check_qt5_distinguished_name_parser check_distinguished_name_parser.cpp) +qt5_add_qtest(check_qt5_cidfontswidthsbuilder check_cidfontswidthsbuilder.cpp) +qt5_add_qtest(check_qt5_overprint check_overprint.cpp) if (NOT WIN32) qt5_add_qtest(check_qt5_pagelabelinfo check_pagelabelinfo.cpp) qt5_add_qtest(check_qt5_strings check_strings.cpp) diff --git a/qt5/tests/check_actualtext.cpp b/qt5/tests/check_actualtext.cpp index 709bb3b..314f918 100644 --- a/qt5/tests/check_actualtext.cpp +++ b/qt5/tests/check_actualtext.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -9,20 +9,25 @@ class TestActualText : public QObject Q_OBJECT public: explicit TestActualText(QObject *parent = nullptr) : QObject(parent) { } + + void checkActualText(Poppler::Document *doc, const QRectF &area, const QString &text); + private slots: void checkActualText1(); void checkActualText2(); - -private: - void checkActualText(Poppler::Document *doc); + void checkActualText2_data(); + void checkAllOrientations(); + void checkAllOrientations_data(); + void checkFakeboldText(); + void checkFakeboldText_data(); }; -void TestActualText::checkActualText(Poppler::Document *doc) +void TestActualText::checkActualText(Poppler::Document *doc, const QRectF &area, const QString &text) { Poppler::Page *page = doc->page(0); QVERIFY(page); - QCOMPARE(page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.")); + QCOMPARE(page->text(area), text); delete page; } @@ -33,13 +38,16 @@ void TestActualText::checkActualText1() doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); - checkActualText(doc); + checkActualText(doc, QRectF {}, QStringLiteral("The slow brown fox jumps over the black dog.")); delete doc; } void TestActualText::checkActualText2() { + QFETCH(QRectF, area); + QFETCH(QString, text); + QFile file(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -47,11 +55,126 @@ void TestActualText::checkActualText2() doc = Poppler::Document::load(&file); QVERIFY(doc); - checkActualText(doc); + checkActualText(doc, area, text); delete doc; } +void TestActualText::checkActualText2_data() +{ + QTest::addColumn("area"); + QTest::addColumn("text"); + + // Line bounding box is [100.000 90.720 331.012110 102.350] + + QTest::newRow("full page") << QRectF {} << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("full line") << QRectF { 50.0, 90.0, 290.0, 20.0 } << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("full line [narrow]") << QRectF { 50.0, 95.0, 290.0, 5.0 } << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("above line") << QRectF { 50.0, 85.0, 290.0, 10.0 } << QString {}; + QTest::newRow("above line mid") << QRectF { 50.0, 90.0, 290.0, 5.0 } << QString {}; + QTest::newRow("first two words") << QRectF { 50.0, 90.0, 100.0, 20.0 } << QStringLiteral("The slow"); + QTest::newRow("first two words [narrow]") << QRectF { 50.0, 95.0, 100.0, 5.0 } << QStringLiteral("The slow"); + QTest::newRow("first character") << QRectF { 103.0, 95.0, 1.0, 5.0 } << QStringLiteral("T"); + QTest::newRow("last two words") << QRectF { 285.0, 90.0, 100.0, 20.0 } << QStringLiteral("black dog."); + QTest::newRow("last character") << QRectF { 320.0, 90.0, 8.0, 20.0 } << QStringLiteral("g"); + QTest::newRow("middle 'fox'") << QRectF { 190.0, 90.0, 15.0, 20.0 } << QStringLiteral("fox"); + QTest::newRow("middle 'x'") << QRectF { 200.0, 90.0, 5.0, 20.0 } << QStringLiteral("x"); +} + +void TestActualText::checkAllOrientations() +{ + QFETCH(int, pageNr); + QFETCH(QRectF, area); + QFETCH(QString, text); + + QString path { TESTDATADIR "/unittestcases/orientation.pdf" }; + std::unique_ptr doc { Poppler::Document::load(path) }; + QVERIFY(doc); + + std::unique_ptr page { doc->page(pageNr) }; + QVERIFY(page); + + QCOMPARE(page->text(area), text); +} + +void TestActualText::checkAllOrientations_data() +{ + QTest::addColumn("pageNr"); + QTest::addColumn("area"); + QTest::addColumn("text"); + + QTest::newRow("Portrait") << 0 << QRectF {} << QStringLiteral("Portrait"); + QTest::newRow("Landscape") << 1 << QRectF {} << QStringLiteral("Landscape"); + QTest::newRow("Upside down") << 2 << QRectF {} << QStringLiteral("Upside down"); + QTest::newRow("Seacape") << 3 << QRectF {} << QStringLiteral("Seascape"); + + QTest::newRow("Portrait A4 rect") << 0 << QRectF { 0, 0, 595, 842 } << QStringLiteral("Portrait"); + QTest::newRow("Landscape A4 rect") << 1 << QRectF { 0, 0, 842, 595 } << QStringLiteral("Landscape"); + QTest::newRow("Upside down A4 rect") << 2 << QRectF { 0, 0, 595, 842 } << QStringLiteral("Upside down"); + QTest::newRow("Seacape A4 rect") << 3 << QRectF { 0, 0, 842, 595 } << QStringLiteral("Seascape"); + + QTest::newRow("Portrait line rect") << 0 << QRectF { 30, 30, 60, 20 } << QStringLiteral("Portrait"); + QTest::newRow("Landscape line rect") << 1 << QRectF { 790, 30, 20, 80 } << QStringLiteral("Landscape"); + QTest::newRow("Upside down line rect") << 2 << QRectF { 485, 790, 75, 20 } << QStringLiteral("Upside down"); + QTest::newRow("Seacape line rect") << 3 << QRectF { 30, 500, 20, 70 } << QStringLiteral("Seascape"); + + QTest::newRow("Portrait small rect B") << 0 << QRectF { 30, 35, 10, 10 } << QStringLiteral("P"); + QTest::newRow("Portrait small rect E") << 0 << QRectF { 80, 35, 10, 10 } << QStringLiteral("t"); + QTest::newRow("Landscape small rect B") << 1 << QRectF { 800, 30, 10, 10 } << QStringLiteral("L"); + QTest::newRow("Landscape small rect E") << 1 << QRectF { 800, 90, 10, 10 } << QStringLiteral("e"); + QTest::newRow("Upside down small rect B") << 2 << QRectF { 550, 800, 10, 10 } << QStringLiteral("U"); + QTest::newRow("Upside down small rect E") << 2 << QRectF { 485, 800, 10, 10 } << QStringLiteral("n"); + QTest::newRow("Seacape small rect B") << 3 << QRectF { 40, 550, 10, 10 } << QStringLiteral("S"); + QTest::newRow("Seacape small rect E") << 3 << QRectF { 40, 510, 10, 10 } << QStringLiteral("p"); +} + +void TestActualText::checkFakeboldText() +{ + QFETCH(int, pageNr); + QFETCH(QRectF, area); + QFETCH(QString, text); + + QString path { TESTDATADIR "/unittestcases/fakebold.pdf" }; + std::unique_ptr doc { Poppler::Document::load(path) }; + QVERIFY(doc); + + std::unique_ptr page { doc->page(pageNr) }; + QVERIFY(page); + + QCOMPARE(page->text(area), text); +} + +void TestActualText::checkFakeboldText_data() +{ + QTest::addColumn("pageNr"); + QTest::addColumn("area"); + QTest::addColumn("text"); + + QTest::newRow("Upright line 1") << 0 << QRectF { 0, 0, 595, 80 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Upright line 2") << 0 << QRectF { 0, 80, 595, 80 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Upright line 3") << 0 << QRectF { 0, 140, 595, 80 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Upright line 4") << 0 << QRectF { 0, 220, 595, 80 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Upright line 5") << 0 << QRectF { 0, 300, 595, 80 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 90' line 1") << 1 << QRectF { 510, 0, 80, 842 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 90' line 2") << 1 << QRectF { 430, 0, 80, 842 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 90' line 3") << 1 << QRectF { 350, 0, 80, 842 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 90' line 4") << 1 << QRectF { 270, 0, 80, 842 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 90' line 5") << 1 << QRectF { 190, 0, 80, 842 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 180' line 1") << 2 << QRectF { 0, 760, 595, 80 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 180' line 2") << 2 << QRectF { 0, 680, 595, 80 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 180' line 3") << 2 << QRectF { 0, 600, 595, 80 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 180' line 4") << 2 << QRectF { 0, 520, 595, 80 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 180' line 5") << 2 << QRectF { 0, 440, 595, 80 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 270' line 1") << 3 << QRectF { 20, 0, 80, 842 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 270' line 2") << 3 << QRectF { 100, 0, 80, 842 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 270' line 3") << 3 << QRectF { 160, 0, 80, 842 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 270' line 4") << 3 << QRectF { 240, 0, 80, 842 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 270' line 5") << 3 << QRectF { 320, 0, 80, 842 } << QStringLiteral("5 Quoted \"fakebold\" word."); +} + QTEST_GUILESS_MAIN(TestActualText) #include "check_actualtext.moc" diff --git a/qt5/tests/check_annotations.cpp b/qt5/tests/check_annotations.cpp index 0e8e88e..e922d8b 100644 --- a/qt5/tests/check_annotations.cpp +++ b/qt5/tests/check_annotations.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -35,8 +35,8 @@ void TestAnnotations::checkQColorPrecision() bool precisionOk = true; for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) { double normalized = static_cast(i) / static_cast(std::numeric_limits::max()); - const std::unique_ptr serialized = GooString::format("{0:.5f}", normalized); - double deserialized = gatof(serialized->c_str()); + const std::string serialized = GooString::format("{0:.5f}", normalized); + double deserialized = gatof(serialized.c_str()); uint16_t denormalized = std::round(deserialized * std::numeric_limits::max()); if (static_cast(i) != denormalized) { precisionOk = false; diff --git a/qt5/tests/check_attachments.cpp b/qt5/tests/check_attachments.cpp index 8e250df..4a7359c 100644 --- a/qt5/tests/check_attachments.cpp +++ b/qt5/tests/check_attachments.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_cidfontswidthsbuilder.cpp b/qt5/tests/check_cidfontswidthsbuilder.cpp new file mode 100644 index 0000000..1986934 --- /dev/null +++ b/qt5/tests/check_cidfontswidthsbuilder.cpp @@ -0,0 +1,100 @@ +//======================================================================== +// +// check_cidfontswidthsbuilder.cpp +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#include "CIDFontsWidthsBuilder.h" + +#include + +class TestCIDFontsWidthsBuilder : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; +private Q_SLOTS: + void testEmpty(); + void testSingle(); + void testSimpleSequence(); +}; + +void TestCIDFontsWidthsBuilder::testEmpty() +{ + CIDFontsWidthsBuilder b; + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 0); +} + +static bool compare(const CIDFontsWidthsBuilder::Segment &segment1, const CIDFontsWidthsBuilder::Segment &segment2) +{ + return std::visit( + [](const auto &s1, const auto &s2) { + using T1 = std::decay_t; + using T2 = std::decay_t; + if constexpr (!std::is_same_v) { + return false; + } else if constexpr (std::is_same_v) { + return s1.first == s2.first && s1.widths == s2.widths; + } else if constexpr (std::is_same_v) { + return s1.first == s2.first && s1.last == s2.last && s1.width == s2.width; + } else { + return false; + } + }, + segment1, segment2); +} + +void TestCIDFontsWidthsBuilder::testSingle() +{ + CIDFontsWidthsBuilder b; + b.addWidth(0, 10); + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 1); + auto segment0 = CIDFontsWidthsBuilder::ListSegment { 0, { 10 } }; + QVERIFY(compare(segments[0], segment0)); +} + +void TestCIDFontsWidthsBuilder::testSimpleSequence() +{ + CIDFontsWidthsBuilder b; + for (int i = 0; i < 2; i++) { // repeat to verify that takeSegments resets + b.addWidth(0, 10); + b.addWidth(1, 10); + b.addWidth(2, 10); + b.addWidth(3, 10); + b.addWidth(4, 10); + b.addWidth(5, 20); + b.addWidth(6, 21); + b.addWidth(7, 21); + b.addWidth(8, 20); + b.addWidth(9, 10); + b.addWidth(10, 10); + b.addWidth(11, 10); + b.addWidth(12, 10); + b.addWidth(13, 10); + b.addWidth(14, 20); + b.addWidth(15, 21); + b.addWidth(16, 21); + b.addWidth(17, 20); + b.addWidth(19, 20); + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 5); + auto segment0 = CIDFontsWidthsBuilder::RangeSegment { 0, 4, 10 }; + QVERIFY(compare(segments[0], segment0)); + auto segment1 = CIDFontsWidthsBuilder::ListSegment { 5, { 20, 21, 21, 20 } }; + QVERIFY(compare(segments[1], segment1)); + auto segment2 = CIDFontsWidthsBuilder::RangeSegment { 9, 13, 10 }; + QVERIFY(compare(segments[2], segment2)); + auto segment3 = CIDFontsWidthsBuilder::ListSegment { 14, { 20, 21, 21, 20 } }; + QVERIFY(compare(segments[3], segment3)); + auto segment4 = CIDFontsWidthsBuilder::ListSegment { 19, { 20 } }; + QVERIFY(compare(segments[4], segment4)); + } +} + +QTEST_GUILESS_MAIN(TestCIDFontsWidthsBuilder); +#include "check_cidfontswidthsbuilder.moc" diff --git a/qt5/tests/check_dateConversion.cpp b/qt5/tests/check_dateConversion.cpp index acbd4b8..e6abc84 100644 --- a/qt5/tests/check_dateConversion.cpp +++ b/qt5/tests/check_dateConversion.cpp @@ -1,4 +1,4 @@ -#include +#include Q_DECLARE_METATYPE(QDate) Q_DECLARE_METATYPE(QTime) diff --git a/qt5/tests/check_distinguished_name_parser.cpp b/qt5/tests/check_distinguished_name_parser.cpp new file mode 100644 index 0000000..318e40f --- /dev/null +++ b/qt5/tests/check_distinguished_name_parser.cpp @@ -0,0 +1,165 @@ +//======================================================================== +// +// check_distinguished_name_parser.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== +#include "DistinguishedNameParser.h" + +#include +#include + +class TestDistinguishedNameParser : public QObject +{ + Q_OBJECT +public: + explicit TestDistinguishedNameParser(QObject *parent = nullptr) : QObject(parent) { } +private slots: + // The big set of input/output. Several of the helper functions can be tested independently + void testParser(); + void testParser_data(); + + void testRemoveLeadingSpaces(); + void testRemoveLeadingSpaces_data(); + + void testRemoveTrailingSpaces(); + void testRemoveTrailingSpaces_data(); + + void testParseHexString(); + void testParseHexString_data(); +}; + +Q_DECLARE_METATYPE(DN::Result); +Q_DECLARE_METATYPE(std::string); +Q_DECLARE_METATYPE(std::optional); + +void TestDistinguishedNameParser::testParser() +{ + QFETCH(std::string, inputData); + QFETCH(DN::Result, expectedResult); + + auto result = DN::parseString(inputData); + QCOMPARE(result, expectedResult); +} + +void TestDistinguishedNameParser::testParser_data() +{ + QTest::addColumn("inputData"); + QTest::addColumn("expectedResult"); + + QTest::newRow("empty") << std::string {} << DN::Result {}; + QTest::newRow("CN=Simple") << std::string { "CN=Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Name with spaces") << std::string { "CN=Name with spaces" } << DN::Result { { "CN", "Name with spaces" } }; + QTest::newRow("CN=Simple,O=Silly") << std::string { "CN=Simple,O=Silly" } << DN::Result { { "CN", "Simple" }, { "O", "Silly" } }; + QTest::newRow("CN=Steve Kille,O=Isode Limited,C=GB") << std::string { "CN=Steve Kille,O=Isode Limited,C=GB" } << DN::Result { { "CN", "Steve Kille" }, { "O", "Isode Limited" }, { "C", "GB" } }; + QTest::newRow("CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US") + << std::string { "CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US" } << DN::Result { { "CN", "some.user@example.com" }, { "O", "MyCompany" }, { "L", "San Diego" }, { "ST", "California" }, { "C", "US" } }; + QTest::newRow("Multi valued") << std::string { "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US" } + << DN::Result { { "OU", "Sales" }, { "CN", "J. Smith" }, { "O", "Widget Inc." }, { "C", "US" } }; // This is technically wrong, but probably good enough for now + QTest::newRow("Escaping comma") << std::string { "CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB" } << DN::Result { { "CN", "L. Eagle" }, { "O", "Sue, Grabbit and Runn" }, { "C", "GB" } }; + QTest::newRow("Escaped trailing space") << std::string { "CN=Trailing space\\ " } << DN::Result { { "CN", "Trailing space " } }; + QTest::newRow("Escaped quote") << std::string { "CN=Quotation \\\" Mark" } << DN::Result { { "CN", "Quotation \" Mark" } }; + + QTest::newRow("CN=Simple with escaping") << std::string { R"(CN=S\69mpl\65\7A)" } << DN::Result { { "CN", "Simplez" } }; + QTest::newRow(R"(SN=Lu\C4\8Di\C4\87)") << std::string { R"(SN=Lu\C4\8Di\C4\87)" } << DN::Result { { "SN", "Lučić" } }; + QTest::newRow("CN=\"Quoted name\"") << std::string { "CN=\"Quoted name\"" } << DN::Result { { "CN", "Quoted name" } }; + QTest::newRow("CN=\" Leading and trailing spacees \"") << std::string { "CN=\" Leading and trailing spaces \"" } << DN::Result { { "CN", " Leading and trailing spaces " } }; + QTest::newRow("Comma in quotes") << std::string { "CN=\"Comma, inside\"" } << DN::Result { { "CN", "Comma, inside" } }; + QTest::newRow("forbidden chars in quotes") << std::string { R"(CN="Forbidden !@#$%&*()<>[]{},.?/\| chars")" } << DN::Result { { "CN", "Forbidden !@#$%&*()<>[]{},.?/\\| chars" } }; + QTest::newRow("Quoted quotation") << std::string { R"(CN="Quotation \" Mark")" } << DN::Result { { "CN", "Quotation \" Mark" } }; + QTest::newRow("Quoted quotation") << std::string { R"(CN="Quotation \" Mark\" Multiples")" } << DN::Result { { "CN", "Quotation \" Mark\" Multiples" } }; + + QTest::newRow("frompdf1") << std::string { "2.5.4.97=#5553742D49644E722E20444520313233343735323233,CN=TeleSec PKS eIDAS QES CA 5,O=Deutsche Telekom AG,C=DE" } + << DN::Result { { "2.5.4.97", "USt-IdNr. DE 123475223" }, { "CN", "TeleSec PKS eIDAS QES CA 5" }, { "O", "Deutsche Telekom AG" }, { "C", "DE" } }; + QTest::newRow("frompdf2") << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,2.5.4.42=#5765726E6572,2.5.4.4=#4B6F6368,C=DE" } + << DN::Result { { "SerialNumber", "4" }, { "CN", "Koch, Werner" }, { "GN", "Werner" }, { "SN", "Koch" }, { "C", "DE" } }; + QTest::newRow("frompdf2a") << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,oid.2.5.4.42=#5765726E6572,OID.2.5.4.4=#4B6F6368,C=DE" } + << DN::Result { { "SerialNumber", "4" }, { "CN", "Koch, Werner" }, { "GN", "Werner" }, { "SN", "Koch" }, { "C", "DE" } }; + + // weird spacing + QTest::newRow("CN =Simple") << std::string { "CN =Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN= Simple") << std::string { "CN= Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple ") << std::string { "CN=Simple " } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple,") << std::string { "CN=Simple," } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple, O=Silly") << std::string { "CN=Simple, O=Silly" } << DN::Result { { "CN", "Simple" }, { "O", "Silly" } }; + + // various malformed + QTest::newRow("CN=Simple\\") << std::string { "CN=Simple\\" } << DN::Result {}; + QTest::newRow("CN=") << std::string { "CN=" } << DN::Result {}; + QTest::newRow("CN=Simple\\X") << std::string { "CN=Simple\\X" } << DN::Result {}; + QTest::newRow("CN=Simple, O") << std::string { "CN=Simple, O" } << DN::Result {}; + QTest::newRow("CN=Sim\"ple") << std::string { "CN=Sim\"ple, O" } << DN::Result {}; + QTest::newRow("CN=Simple\\a") << std::string { "CN=Simple\\a" } << DN::Result {}; + QTest::newRow("=Simple") << std::string { "=Simple" } << DN::Result {}; + QTest::newRow("CN=\"Simple") << std::string { "CN=\"Simple" } << DN::Result {}; + QTest::newRow("CN=\"Simple") << std::string { "CN=\"Simple\\" } << DN::Result {}; + QTest::newRow("unquoted quotation in quotation") << std::string { R"("CN="Quotation " Mark")" } << DN::Result {}; +} + +void TestDistinguishedNameParser::testRemoveLeadingSpaces() +{ + QFETCH(std::string, input); + QFETCH(std::string, expectedOutput); + + auto result = DN::detail::removeLeadingSpaces(input); + QCOMPARE(result, expectedOutput); +} +void TestDistinguishedNameParser::testRemoveLeadingSpaces_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedOutput"); + + QTest::newRow("Empty") << std::string {} << std::string {}; + QTest::newRow("No leading spaces") << std::string { "horse" } << std::string { "horse" }; + QTest::newRow("Some spaces") << std::string { " horse" } << std::string { "horse" }; + QTest::newRow("Some leading and trailing") << std::string { " horse " } << std::string { "horse " }; +} + +void TestDistinguishedNameParser::testRemoveTrailingSpaces() +{ + QFETCH(std::string, input); + QFETCH(std::string, expectedOutput); + + auto result = DN::detail::removeTrailingSpaces(input); + QCOMPARE(result, expectedOutput); +} +void TestDistinguishedNameParser::testRemoveTrailingSpaces_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedOutput"); + + QTest::newRow("Empty") << std::string {} << std::string {}; + QTest::newRow("No leading spaces") << std::string { "horse" } << std::string { "horse" }; + QTest::newRow("Some spaces") << std::string { "horse " } << std::string { "horse" }; + QTest::newRow("Some leading and trailing") << std::string { " horse " } << std::string { " horse" }; +} + +void TestDistinguishedNameParser::testParseHexString() +{ + QFETCH(std::string, input); + QFETCH(std::optional, expectedOutput); + + auto result = DN::detail::parseHexString(input); + QCOMPARE(result, expectedOutput); +} + +void TestDistinguishedNameParser::testParseHexString_data() +{ + QTest::addColumn("input"); + QTest::addColumn>("expectedOutput"); + + QTest::newRow("4") << std::string { "34" } << std::optional("4"); + QTest::newRow("Koch") << std::string { "4B6F6368" } << std::optional("Koch"); + QTest::newRow("USt-IdNr. DE 123475223") << std::string { "5553742D49644E722E20444520313233343735323233" } << std::optional("USt-IdNr. DE 123475223"); + + // various baddies + QTest::newRow("empty") << std::string {} << std::optional {}; + QTest::newRow("FFF") << std::string { "FFF" } << std::optional {}; + QTest::newRow("F") << std::string { "F" } << std::optional {}; + QTest::newRow("XX") << std::string { "XX" } << std::optional {}; +} + +QTEST_GUILESS_MAIN(TestDistinguishedNameParser); +#include "check_distinguished_name_parser.moc" diff --git a/qt5/tests/check_fonts.cpp b/qt5/tests/check_fonts.cpp index f3df1b2..bb7983d 100644 --- a/qt5/tests/check_fonts.cpp +++ b/qt5/tests/check_fonts.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_forms.cpp b/qt5/tests/check_forms.cpp index 0695936..7d81445 100644 --- a/qt5/tests/check_forms.cpp +++ b/qt5/tests/check_forms.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -226,7 +226,7 @@ void TestForms::testSetAppearanceText() Poppler::FormFieldText *fft = static_cast(field); - const QString textToSet = "HOLA" + fft->name(); + const QString textToSet = QStringLiteral("HOLA") + fft->name(); fft->setAppearanceText(textToSet); Dict *dict = Poppler::FormFieldData::getFormWidget(fft)->getObj()->getDict(); diff --git a/qt5/tests/check_goostring.cpp b/qt5/tests/check_goostring.cpp index cfb14c2..110bf17 100644 --- a/qt5/tests/check_goostring.cpp +++ b/qt5/tests/check_goostring.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "goo/GooString.h" @@ -64,60 +64,61 @@ void TestGooString::testInsert() void TestGooString::testFormat() { { - const std::unique_ptr goo(GooString::format("{0:d},{1:x}", 1, 0xF)); - QCOMPARE(goo->c_str(), "1,f"); + const std::string goo(GooString::format("{0:d},{1:x}", 1, 0xF)); + QCOMPARE(goo.c_str(), "1,f"); } { - const std::unique_ptr goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA)); - QCOMPARE(goo->c_str(), "10,a,A,12,1010, "); + const std::string goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA)); + QCOMPARE(goo.c_str(), "10,a,A,12,1010, "); } { - const std::unique_ptr goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); - QCOMPARE(goo->c_str(), "-10,-a,-A,-12,-1010"); + const std::string goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); + QCOMPARE(goo.c_str(), "-10,-a,-A,-12,-1010"); } { - const std::unique_ptr goo(GooString::format("{0:c}{1:c}{2:c}{3:c}", 'T', (char)'E', (short)'S', (int)'T')); - QCOMPARE(goo->c_str(), "TEST"); + const std::string goo(GooString::format("{0:c}{1:c}{2:c}{3:c}", 'T', (char)'E', (short)'S', (int)'T')); + QCOMPARE(goo.c_str(), "TEST"); - const std::unique_ptr goo2(GooString::format("{0:s} {1:t}", "TEST", goo.get())); - QCOMPARE(goo2->c_str(), "TEST TEST"); + GooString gooGoo(goo); + const std::string goo2(GooString::format("{0:s} {1:t}", "TEST", &gooGoo)); + QCOMPARE(goo2.c_str(), "TEST TEST"); } { - const std::unique_ptr goo(GooString::format("{0:ud} {1:d} {2:d}", UINT_MAX, INT_MAX, INT_MIN)); + const std::string goo(GooString::format("{0:ud} {1:d} {2:d}", UINT_MAX, INT_MAX, INT_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr goo(GooString::format("{0:uld} {1:ld} {2:ld}", ULONG_MAX, LONG_MAX, LONG_MIN)); + const std::string goo(GooString::format("{0:uld} {1:ld} {2:ld}", ULONG_MAX, LONG_MAX, LONG_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr goo(GooString::format("{0:ulld} {1:lld} {2:lld}", ULLONG_MAX, LLONG_MAX, LLONG_MIN)); + const std::string goo(GooString::format("{0:ulld} {1:lld} {2:lld}", ULLONG_MAX, LLONG_MAX, LLONG_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012)); - const std::unique_ptr gooF(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1.f, .012f)); - QCOMPARE(gooD->c_str(), "1.0 1 1 | 0.0 0 0.01"); - QCOMPARE(gooF->c_str(), "1.0 1 1 | 0.0 0 0.01"); + const std::string gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012)); + const std::string gooF(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1.f, .012f)); + QCOMPARE(gooD.c_str(), "1.0 1 1 | 0.0 0 0.01"); + QCOMPARE(gooF.c_str(), "1.0 1 1 | 0.0 0 0.01"); } { - const std::unique_ptr goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); - QCOMPARE(goo->c_str(), "0.0120 0.012 0.012"); + const std::string goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); + QCOMPARE(goo.c_str(), "0.0120 0.012 0.012"); } { - const std::unique_ptr goo(GooString::format("{{ SomeText {0:d} }}", 1)); - QCOMPARE(goo->c_str(), "{ SomeText 1 }"); + const std::string goo(GooString::format("{{ SomeText {0:d} }}", 1)); + QCOMPARE(goo.c_str(), "{ SomeText 1 }"); } { - const std::unique_ptr goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); - QCOMPARE(goo->c_str(), "{{ { SomeText 2"); + const std::string goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); + QCOMPARE(goo.c_str(), "{{ { SomeText 2"); } { - const std::unique_ptr goo(GooString::format("SomeText {0:d} }} }}}}", 3)); - QCOMPARE(goo->c_str(), "SomeText 3 } }}"); + const std::string goo(GooString::format("SomeText {0:d} }} }}}}", 3)); + QCOMPARE(goo.c_str(), "SomeText 3 } }}"); } } diff --git a/qt5/tests/check_internal_outline.cpp b/qt5/tests/check_internal_outline.cpp index c12b604..5be368a 100644 --- a/qt5/tests/check_internal_outline.cpp +++ b/qt5/tests/check_internal_outline.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include "Outline.h" #include "PDFDoc.h" @@ -56,10 +57,10 @@ void TestInternalOutline::testCreateOutline() static std::string getTitle(const OutlineItem *item) { - const Unicode *u = item->getTitle(); + std::vector u = item->getTitle(); std::string s; - for (int i = 0; i < item->getTitleLength(); i++) { - s.append(1, (char)u[i]); + for (auto &c : u) { + s.append(1, (char)(c)); } return s; } diff --git a/qt5/tests/check_lexer.cpp b/qt5/tests/check_lexer.cpp index 9c60962..8b7a46f 100644 --- a/qt5/tests/check_lexer.cpp +++ b/qt5/tests/check_lexer.cpp @@ -1,4 +1,4 @@ -#include +#include #include "Object.h" #include "Lexer.h" diff --git a/qt5/tests/check_links.cpp b/qt5/tests/check_links.cpp index 3146901..04eeb9b 100644 --- a/qt5/tests/check_links.cpp +++ b/qt5/tests/check_links.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_metadata.cpp b/qt5/tests/check_metadata.cpp index 8c87e41..235a4ea 100644 --- a/qt5/tests/check_metadata.cpp +++ b/qt5/tests/check_metadata.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_object.cpp b/qt5/tests/check_object.cpp index 08a7f75..cadc006 100644 --- a/qt5/tests/check_object.cpp +++ b/qt5/tests/check_object.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "poppler/Object.h" diff --git a/qt5/tests/check_optcontent.cpp b/qt5/tests/check_optcontent.cpp index b254cdb..38d159d 100644 --- a/qt5/tests/check_optcontent.cpp +++ b/qt5/tests/check_optcontent.cpp @@ -1,4 +1,4 @@ -#include +#include #include "PDFDoc.h" #include "GlobalParams.h" @@ -92,7 +92,7 @@ void TestOptionalContent::checkIsVisible() PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); - OCGs *ocgs = doc->getOptContentConfig(); + const OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); @@ -164,7 +164,7 @@ void TestOptionalContent::checkVisibilitySetting() PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); - OCGs *ocgs = doc->getOptContentConfig(); + const OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); @@ -173,7 +173,7 @@ void TestOptionalContent::checkVisibilitySetting() // In this test, both Ref(21,0) and Ref(28,0) start On, // based on the file settings - Object ref21obj({ 21, 0 }); + Object ref21obj(Ref { 21, 0 }); Ref ref21 = ref21obj.getRef(); OptionalContentGroup *ocgA = ocgs->findOcgByRef(ref21); QVERIFY(ocgA); @@ -181,7 +181,7 @@ void TestOptionalContent::checkVisibilitySetting() QVERIFY((ocgA->getName()->cmp("A")) == 0); QCOMPARE(ocgA->getState(), OptionalContentGroup::On); - Object ref28obj({ 28, 0 }); + Object ref28obj(Ref { 28, 0 }); Ref ref28 = ref28obj.getRef(); OptionalContentGroup *ocgB = ocgs->findOcgByRef(ref28); QVERIFY(ocgB); diff --git a/qt5/tests/check_outline.cpp b/qt5/tests/check_outline.cpp index bc12bc9..92eb529 100644 --- a/qt5/tests/check_outline.cpp +++ b/qt5/tests/check_outline.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_overprint.cpp b/qt5/tests/check_overprint.cpp new file mode 100644 index 0000000..c0d965a --- /dev/null +++ b/qt5/tests/check_overprint.cpp @@ -0,0 +1,41 @@ +#include + +#include + +#include + +class TestOverprint : public QObject +{ + Q_OBJECT +public: + explicit TestOverprint(QObject *parent = nullptr) : QObject(parent) { } +private slots: + void checkOverprintImageRendering(); +}; + +void TestOverprint::checkOverprintImageRendering() +{ + Poppler::Document *doc = Poppler::Document::load(TESTDATADIR "/tests/mask-seams.pdf"); + QVERIFY(doc); + + doc->setRenderHint(Poppler::Document::OverprintPreview, true); + + Poppler::Page *page = doc->page(0); + QVERIFY(page); + + constexpr int width = 600; + constexpr int height = 400; + + QImage img = page->renderToImage(300.0, 300.0, 0, 0, width, height); + QCOMPARE(img.format(), QImage::Format_RGB32); + QCOMPARE(img.width(), width); + QCOMPARE(img.height(), height); + QCOMPARE(img.bytesPerLine(), width * 4); + QCOMPARE(img.sizeInBytes(), width * height * 4); + + delete page; + delete doc; +} + +QTEST_GUILESS_MAIN(TestOverprint) +#include "check_overprint.moc" diff --git a/qt5/tests/check_pagelabelinfo.cpp b/qt5/tests/check_pagelabelinfo.cpp index a8e0f9e..3098bbf 100644 --- a/qt5/tests/check_pagelabelinfo.cpp +++ b/qt5/tests/check_pagelabelinfo.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -31,7 +31,7 @@ void TestPageLabelInfo::testFromDecimal() void TestPageLabelInfo::testFromDecimalUnicode() { std::unique_ptr str(Poppler::QStringToUnicodeGooString(QString::fromLocal8Bit("2342"))); - const auto res = fromDecimal(str->toStr(), str->hasUnicodeMarker()); + const auto res = fromDecimal(str->toStr(), hasUnicodeByteOrderMark(str->toStr())); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } diff --git a/qt5/tests/check_pagelayout.cpp b/qt5/tests/check_pagelayout.cpp index 20c2f0f..3c134d6 100644 --- a/qt5/tests/check_pagelayout.cpp +++ b/qt5/tests/check_pagelayout.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_pagemode.cpp b/qt5/tests/check_pagemode.cpp index 9a0b5e9..78cbc0c 100644 --- a/qt5/tests/check_pagemode.cpp +++ b/qt5/tests/check_pagemode.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_password.cpp b/qt5/tests/check_password.cpp index 94e19b1..b58fe1c 100644 --- a/qt5/tests/check_password.cpp +++ b/qt5/tests/check_password.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -16,6 +16,7 @@ private slots: void password3(); void password4(); void password4b(); + void password5(); }; // BUG:4557 @@ -111,5 +112,17 @@ void TestPassword::password4b() delete doc; } +void TestPassword::password5() +{ + Poppler::Document *doc; + doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/PasswordEncryptedReconstructed.pdf")); + QVERIFY(doc); + QVERIFY(doc->isLocked()); + QVERIFY(!doc->unlock("", "test")); + QVERIFY(!doc->isLocked()); + + delete doc; +} + QTEST_GUILESS_MAIN(TestPassword) #include "check_password.moc" diff --git a/qt5/tests/check_permissions.cpp b/qt5/tests/check_permissions.cpp index ceaf4d6..d6ea7b4 100644 --- a/qt5/tests/check_permissions.cpp +++ b/qt5/tests/check_permissions.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt5/tests/check_search.cpp b/qt5/tests/check_search.cpp index c9bb65e..d93e851 100644 --- a/qt5/tests/check_search.cpp +++ b/qt5/tests/check_search.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -350,6 +350,13 @@ void TestSearch::testAcrossLinesSearch() QCOMPARE(page0->search(str6, l, t, r, b, direction, mode1), true); QCOMPARE(page0->search(str6, l, t, r, b, direction, mode2), true); QCOMPARE(page0->search(str6, l, t, r, b, direction, mode2W), true); + // Check for the case when next line falls in next paragraph. Issue #1475 + const QString across_block = QString::fromUtf8("emacs jose"); // clazy:exclude=qstring-allocations + QCOMPARE(page0->search(across_block, l, t, r, b, direction, empty), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode0), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode1), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode2), true); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode2W), true); // Now for completeness, we will match the full text of two lines const QString full2lines = QString::fromUtf8("Las pruebas se practicarán en vista pública, si bien, excepcionalmente, el Tribunal podrá acordar, mediante providencia, que determinadas pruebas se celebren fuera del acto de juicio"); diff --git a/qt5/tests/check_signature_basics.cpp b/qt5/tests/check_signature_basics.cpp new file mode 100644 index 0000000..0b8e72a --- /dev/null +++ b/qt5/tests/check_signature_basics.cpp @@ -0,0 +1,181 @@ +//======================================================================== +// +// check_signature_basics.cpp +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +// Simple tests of reading signatures +// +// Note that this does not check the actual validity because +// that will have an expiry date, and adding time bombs to unit tests is +// probably not a good idea. +#include +#include "PDFDoc.h" +#include "GlobalParams.h" +#include "SignatureInfo.h" +#include "CryptoSignBackend.h" +#include "config.h" + +class TestSignatureBasics : public QObject +{ + Q_OBJECT +public: + explicit TestSignatureBasics(QObject *parent = nullptr) : QObject(parent) { } + + std::unique_ptr doc; + +private Q_SLOTS: + void init(); + void initTestCase_data(); + void initTestCase() { } + void cleanupTestCase(); + void testSignatureCount(); + void testSignatureSizes(); + void testSignerInfo(); // names and stuff + void testSignedRanges(); +}; + +Q_DECLARE_METATYPE(CryptoSign::Backend::Type); + +void TestSignatureBasics::init() +{ +#ifdef ENABLE_SIGNATURES + QFETCH_GLOBAL(CryptoSign::Backend::Type, backend); + CryptoSign::Factory::setPreferredBackend(backend); + QCOMPARE(CryptoSign::Factory::getActive(), backend); +#endif + + globalParams = std::make_unique(); + doc = std::make_unique(std::make_unique(TESTDATADIR "/unittestcases/pdf-signature-sample-2sigs.pdf")); + QVERIFY(doc); + QVERIFY(doc->isOk()); +} + +void TestSignatureBasics::initTestCase_data() +{ + QTest::addColumn("backend"); + +#ifdef ENABLE_SIGNATURES + const auto availableBackends = CryptoSign::Factory::getAvailable(); + +# ifdef ENABLE_NSS3 + if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::NSS3) != availableBackends.end()) { + QTest::newRow("nss") << CryptoSign::Backend::Type::NSS3; + } else { + QWARN("Compiled with NSS3, but NSS not functional"); + } +# endif +# ifdef ENABLE_GPGME + if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::GPGME) != availableBackends.end()) { + QTest::newRow("gpg") << CryptoSign::Backend::Type::GPGME; + } else { + QWARN("Compiled with GPGME, but GPGME not functional"); + } +# endif +#endif +} + +void TestSignatureBasics::cleanupTestCase() +{ + globalParams.reset(); +} + +void TestSignatureBasics::testSignatureCount() +{ + QVERIFY(doc); + auto signatureFields = doc->getSignatureFields(); + QCOMPARE(signatureFields.size(), 4); + // count active signatures + QVERIFY(!signatureFields[0]->getSignature().empty()); + QVERIFY(!signatureFields[1]->getSignature().empty()); + QVERIFY(signatureFields[2]->getSignature().empty()); + QVERIFY(signatureFields[3]->getSignature().empty()); +} + +void TestSignatureBasics::testSignatureSizes() +{ + auto signatureFields = doc->getSignatureFields(); + // These are not the actual signature lengths, but rather + // the length of the signature field, which is likely + // a padded field. At least the pdf specification suggest to pad + // the field. + // Poppler before 23.04 did not have a padded field, later versions do. + QCOMPARE(signatureFields[0]->getSignature().size(), 10230); // Signature data size is 2340 + QCOMPARE(signatureFields[1]->getSignature().size(), 10196); // Signature data size is 2340 +} + +void TestSignatureBasics::testSignerInfo() +{ + auto signatureFields = doc->getSignatureFields(); + QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" }); + QCOMPARE(signatureFields[0]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached); + auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {}); + signatureFields[0]->validateSignatureResult(); +#ifdef ENABLE_SIGNATURES + QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" }); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256); + QCOMPARE(siginfo0->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8); +#else + QCOMPARE(siginfo0->getSignerName(), std::string {}); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Unknown); +#endif + QCOMPARE(siginfo0->getSigningTime(), time_t(1677570911)); + + QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" }); + QCOMPARE(signatureFields[1]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached); + auto siginfo1 = signatureFields[1]->validateSignatureAsync(false, false, -1 /* now */, false, false, {}); + signatureFields[1]->validateSignatureResult(); +#ifdef ENABLE_SIGNATURES + QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" }); + QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256); + QFETCH_GLOBAL(CryptoSign::Backend::Type, backend); + if (backend == CryptoSign::Backend::Type::GPGME) { + QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8); + } else if (backend == CryptoSign::Backend::Type::NSS3) { + // Not fully sure why it is zero here, but it seems to be. + QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 0); + } +#else + QCOMPARE(siginfo1->getSignerName(), std::string {}); + QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Unknown); +#endif + QCOMPARE(siginfo1->getSigningTime(), time_t(1677840601)); + + QCOMPARE(signatureFields[2]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature2_B_" }); + QCOMPARE(signatureFields[2]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field); + QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" }); + QCOMPARE(signatureFields[3]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field); +} + +void TestSignatureBasics::testSignedRanges() +{ + auto signatureFields = doc->getSignatureFields(); + + Goffset size0; + auto sig0 = signatureFields[0]->getCheckedSignature(&size0); + QVERIFY(sig0); + auto ranges0 = signatureFields[0]->getSignedRangeBounds(); + QCOMPARE(ranges0.size(), 4); + QCOMPARE(ranges0[0], 0); + QCOMPARE(ranges0[1], 24890); + QCOMPARE(ranges0[2], 45352); + QCOMPARE(ranges0[3], 58529); + QVERIFY(ranges0[3] != size0); // signature does not cover all of it + + Goffset size1; + auto sig1 = signatureFields[1]->getCheckedSignature(&size1); + QVERIFY(sig1); + auto ranges1 = signatureFields[1]->getSignedRangeBounds(); + QCOMPARE(ranges1.size(), 4); + QCOMPARE(ranges1[0], 0); + QCOMPARE(ranges1[1], 59257); + QCOMPARE(ranges1[2], 79651); + QCOMPARE(ranges1[3], 92773); + QCOMPARE(ranges1[3], size1); // signature does cover all of it +} + +QTEST_GUILESS_MAIN(TestSignatureBasics) +#include "check_signature_basics.moc" diff --git a/qt5/tests/check_strings.cpp b/qt5/tests/check_strings.cpp index 5772fd8..4c80aea 100644 --- a/qt5/tests/check_strings.cpp +++ b/qt5/tests/check_strings.cpp @@ -1,6 +1,8 @@ /* * Copyright (C) 2010, 2011, Pino Toscano * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, + * Copyright (C) 2024 Oliver Sander + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +19,13 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include +#include Q_DECLARE_METATYPE(GooString *) Q_DECLARE_METATYPE(Unicode *) @@ -34,6 +37,11 @@ class TestStrings : public QObject public: explicit TestStrings(QObject *parent = nullptr) : QObject(parent) { } + GooString *newGooString(const char *s); + GooString *newGooString(const char *s, int l); + + QVector m_gooStrings; + private slots: void initTestCase(); void cleanupTestCase(); @@ -45,12 +53,6 @@ private slots: void check_QStringToUnicodeGooString(); void check_QStringToGooString_data(); void check_QStringToGooString(); - -private: - GooString *newGooString(const char *s); - GooString *newGooString(const char *s, int l); - - QVector m_gooStrings; }; void TestStrings::initTestCase() @@ -189,17 +191,15 @@ void TestStrings::check_QStringToUnicodeGooString() QFETCH(QString, string); QFETCH(QByteArray, result); - GooString *goo = Poppler::QStringToUnicodeGooString(string); + std::unique_ptr goo = Poppler::QStringToUnicodeGooString(string); if (string.isEmpty()) { QVERIFY(goo->toStr().empty()); QCOMPARE(goo->getLength(), 0); } else { - QVERIFY(goo->hasUnicodeMarker()); + QVERIFY(hasUnicodeByteOrderMark(goo->toStr())); QCOMPARE(goo->getLength(), string.length() * 2 + 2); QCOMPARE(result, QByteArray::fromRawData(goo->c_str() + 2, goo->getLength() - 2)); } - - delete goo; } void TestStrings::check_QStringToGooString_data() @@ -218,10 +218,8 @@ void TestStrings::check_QStringToGooString() QFETCH(QString, string); QFETCH(GooString *, result); - GooString *goo = Poppler::QStringToGooString(string); + const std::unique_ptr goo = Poppler::QStringToGooString(string); QCOMPARE(goo->c_str(), result->c_str()); - - delete goo; } GooString *TestStrings::newGooString(const char *s) diff --git a/qt5/tests/check_stroke_opacity.cpp b/qt5/tests/check_stroke_opacity.cpp index 8357f62..50b4b7d 100644 --- a/qt5/tests/check_stroke_opacity.cpp +++ b/qt5/tests/check_stroke_opacity.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include diff --git a/qt5/tests/check_utf8document.cpp b/qt5/tests/check_utf8document.cpp new file mode 100644 index 0000000..32c95f6 --- /dev/null +++ b/qt5/tests/check_utf8document.cpp @@ -0,0 +1,58 @@ +#include + +#include "PDFDoc.h" +#include "GlobalParams.h" + +#include "Outline.h" +#include "poppler-private.h" + +class TestUtf8Document : public QObject +{ + Q_OBJECT +public: + explicit TestUtf8Document(QObject *parent = nullptr) : QObject(parent) { } +private Q_SLOTS: + void checkStrings(); +}; + +inline QString outlineItemTitle(OutlineItem *item) +{ + if (!item) { + return {}; + } + const std::vector &title = item->getTitle(); + return QString::fromUcs4(title.data(), title.size()); +} + +void TestUtf8Document::checkStrings() +{ + + globalParams = std::make_unique(); + auto doc = std::make_unique(std::make_unique(TESTDATADIR "/unittestcases/pdf20-utf8-test.pdf")); + QVERIFY(doc); + QVERIFY(doc->isOk()); + + QVERIFY(doc->getOptContentConfig() && doc->getOptContentConfig()->hasOCGs()); + + QCOMPARE(Poppler::UnicodeParsedString(doc->getDocInfoTitle().get()), QString::fromUtf8("表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀")); // clazy:exclude=qstring-allocations + + QSet expectedNames { QString::fromUtf8("گچپژ"), QString::fromUtf8("Layer 1") }; // clazy:exclude=qstring-allocations + QSet foundNames; + + for (auto &[ref, group] : doc->getOptContentConfig()->getOCGs()) { + foundNames.insert(Poppler::UnicodeParsedString(group->getName())); + } + QCOMPARE(expectedNames, foundNames); + + auto outlineItems = doc->getOutline()->getItems(); + QVERIFY(outlineItems); + QCOMPARE(outlineItems->size(), 3); + + QCOMPARE(outlineItemTitle(outlineItems->at(0)), QString::fromUtf8("PDF 2.0 with UTF-8 test file")); // clazy:exclude=qstring-allocations + QCOMPARE(outlineItemTitle(outlineItems->at(1)), QStringLiteral(u"\u202A\u202Atest\u202A")); + QCOMPARE(outlineItemTitle(outlineItems->at(2)), QString::fromUtf8("🌈️\n" /*emoji rainbow flag*/)); // clazy:exclude=qstring-allocations +} + +QTEST_GUILESS_MAIN(TestUtf8Document) + +#include "check_utf8document.moc" diff --git a/qt5/tests/check_utf_conversion.cpp b/qt5/tests/check_utf_conversion.cpp index 73c684e..99efc48 100644 --- a/qt5/tests/check_utf_conversion.cpp +++ b/qt5/tests/check_utf_conversion.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -22,21 +22,6 @@ private slots: void testUnicodeLittleEndian(); }; -static bool compare(const char *a, const char *b) -{ - return strcmp(a, b) == 0; -} - -static bool compare(const uint16_t *a, const uint16_t *b) -{ - while (*a && *b) { - if (*a++ != *b++) { - return false; - } - } - return *a == *b; -} - static bool compare(const Unicode *a, const char *b, int len) { for (int i = 0; i < len; i++) { @@ -79,49 +64,36 @@ void TestUTFConversion::testUTF_data() void TestUTFConversion::testUTF() { - char utf8Buf[1000]; - char *utf8String; - uint16_t utf16Buf[1000]; - uint16_t *utf16String; + std::string utf8String; int len; QFETCH(QString, s); - char *str = strdup(s.toUtf8().constData()); + const std::string str = s.toStdString(); // UTF-8 to UTF-16 len = utf8CountUtf16CodeUnits(str); QCOMPARE(len, s.size()); // QString size() returns number of code units, not code points - Q_ASSERT(len < (int)sizeof(utf16Buf)); // if this fails, make utf16Buf larger - len = utf8ToUtf16(str, utf16Buf); - QVERIFY(compare(utf16Buf, s.utf16())); + std::u16string utf16String = utf8ToUtf16(str); + QCOMPARE(utf16String, s.toStdU16String()); QCOMPARE(len, s.size()); - utf16String = utf8ToUtf16(str); - QVERIFY(compare(utf16String, s.utf16())); - free(utf16String); - - std::string sUtf8(str); - std::unique_ptr gsUtf16_a(utf8ToUtf16WithBom(sUtf8)); + std::string gsUtf16_a(utf8ToUtf16WithBom(str)); std::unique_ptr gsUtf16_b(Poppler::QStringToUnicodeGooString(s)); - QCOMPARE(gsUtf16_a->cmp(gsUtf16_b.get()), 0); + QCOMPARE(gsUtf16_b->cmp(gsUtf16_a), 0); // UTF-16 to UTF-8 len = utf16CountUtf8Bytes(s.utf16()); - QCOMPARE(len, (int)strlen(str)); - Q_ASSERT(len < (int)sizeof(utf8Buf)); // if this fails, make utf8Buf larger + QCOMPARE(len, str.size()); - len = utf16ToUtf8(s.utf16(), utf8Buf); - QVERIFY(compare(utf8Buf, str)); - QCOMPARE(len, (int)strlen(str)); + utf8String = utf16ToUtf8(s.utf16(), INT_MAX); + QCOMPARE(utf8String, str); + QCOMPARE(len, str.size()); utf8String = utf16ToUtf8(s.utf16()); - QVERIFY(compare(utf8String, str)); - free(utf8String); - - free(str); + QCOMPARE(utf8String, str); } void TestUTFConversion::testUnicodeToAscii7() @@ -131,24 +103,19 @@ void TestUTFConversion::testUnicodeToAscii7() // Test string is one 'Registered' and twenty 'Copyright' chars // so it's long enough to reproduce the bug given that glibc // malloc() always returns 8-byte aligned memory addresses. - GooString *goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations - - Unicode *in; - const int in_len = TextStringToUCS4(goo->toStr(), &in); + std::unique_ptr goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations - delete goo; + const std::vector in = TextStringToUCS4(goo->toStr()); int in_norm_len; int *in_norm_idx; - Unicode *in_norm = unicodeNormalizeNFKC(in, in_len, &in_norm_len, &in_norm_idx, true); - - free(in); + Unicode *in_norm = unicodeNormalizeNFKC(in.data(), in.size(), &in_norm_len, &in_norm_idx, true); Unicode *out; int out_len; int *out_ascii_idx; - unicodeToAscii7(in_norm, in_norm_len, &out, &out_len, in_norm_idx, &out_ascii_idx); + unicodeToAscii7(std::span(in_norm, in_norm_len), &out, &out_len, in_norm_idx, &out_ascii_idx); free(in_norm); free(in_norm_idx); @@ -174,25 +141,24 @@ void TestUTFConversion::testUnicodeLittleEndian() // Let's assert both GooString's are different QVERIFY(GooUTF16LE != GooUTF16BE); - Unicode *UCS4fromLE, *UCS4fromBE; - const int len1 = TextStringToUCS4(GooUTF16LE, &UCS4fromLE); - const int len2 = TextStringToUCS4(GooUTF16BE, &UCS4fromBE); + const std::vector UCS4fromLE = TextStringToUCS4(GooUTF16LE); + const std::vector UCS4fromBE = TextStringToUCS4(GooUTF16BE); // len is 4 because TextStringToUCS4() removes the two leading Byte Order Mark (BOM) code points - QCOMPARE(len1, len2); - QCOMPARE(len1, 4); + QCOMPARE(UCS4fromLE.size(), UCS4fromBE.size()); + QCOMPARE(UCS4fromLE.size(), 4); // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same - for (int i = 0; i < len1; i++) { + for (size_t i = 0; i < UCS4fromLE.size(); i++) { QCOMPARE(UCS4fromLE[i], UCS4fromBE[i]); } const QString expected = QString::fromUtf8("HI!☑"); // clazy:exclude=qstring-allocations // Do some final verifications, checking the strings to be "HI!" - QVERIFY(*UCS4fromLE == *UCS4fromBE); - QVERIFY(compare(UCS4fromLE, expected.utf16(), len1)); - QVERIFY(compare(UCS4fromBE, expected.utf16(), len1)); + QVERIFY(UCS4fromLE == UCS4fromBE); + QVERIFY(compare(UCS4fromLE.data(), expected.utf16(), UCS4fromLE.size())); + QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromLE.size())); } QTEST_GUILESS_MAIN(TestUTFConversion) diff --git a/qt5/tests/poppler-forms.cpp b/qt5/tests/poppler-forms.cpp index 5950969..8d927e8 100644 --- a/qt5/tests/poppler-forms.cpp +++ b/qt5/tests/poppler-forms.cpp @@ -123,6 +123,8 @@ static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationI case Poppler::SignatureValidationInfo::CertificateNotVerified: out << "NotVerifiedYet"; break; + case Poppler::SignatureValidationInfo::CertificateVerificationInProgress: + out << "InProgress"; } return out; } @@ -252,9 +254,10 @@ int main(int argc, char **argv) case Poppler::FormField::FormSignature: { const Poppler::FormFieldSignature *signatureForm = static_cast(form); - const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate); + const Poppler::SignatureValidationInfo svi = signatureForm->validateAsync(Poppler::FormFieldSignature::ValidateVerifyCertificate).first; + const Poppler::SignatureValidationInfo::CertificateStatus certStatus = signatureForm->validateResult(); std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl; - std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl; + std::cout << "\t\t\tCertificateStatus: " << certStatus << std::endl; if (svi.signerName().isEmpty() == false) { std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl; } else { diff --git a/qt5/tests/stress-poppler-dir.cpp b/qt5/tests/stress-poppler-dir.cpp index 8cac185..26bc979 100644 --- a/qt5/tests/stress-poppler-dir.cpp +++ b/qt5/tests/stress-poppler-dir.cpp @@ -19,7 +19,7 @@ int main(int argc, char **argv) foreach (const QString &fileName, directory.entryList()) { if (fileName.endsWith(QStringLiteral("pdf"))) { qDebug() << "Doing" << fileName.toLatin1().data() << ":"; - Poppler::Document *doc = Poppler::Document::load(directory.canonicalPath() + "/" + fileName); + Poppler::Document *doc = Poppler::Document::load(directory.canonicalPath() + '/' + fileName); if (!doc) { qWarning() << "doc not loaded"; } else if (doc->isLocked()) { diff --git a/qt5/tests/stress-poppler-qt5.cpp b/qt5/tests/stress-poppler-qt5.cpp index 31795f9..fb222d4 100644 --- a/qt5/tests/stress-poppler-qt5.cpp +++ b/qt5/tests/stress-poppler-qt5.cpp @@ -30,7 +30,7 @@ int main(int argc, char **argv) if (excludeSubDirs.contains(subdir)) { // then skip it } else { - QString path = "./pdfdb/" + subdir + "/data.pdf"; + QString path = QStringLiteral("./pdfdb/") + subdir + QStringLiteral("/data.pdf"); std::cout << "Doing " << path.toLatin1().data() << " :"; Poppler::Document *doc = Poppler::Document::load(path); if (!doc) { diff --git a/qt5/tests/stress-threads-qt5.cpp b/qt5/tests/stress-threads-qt5.cpp index 164127b..070d69c 100644 --- a/qt5/tests/stress-threads-qt5.cpp +++ b/qt5/tests/stress-threads-qt5.cpp @@ -61,7 +61,7 @@ static Poppler::Page *loadRandomPage(Poppler::Document *document) return loadPage(document, qrand() % document->numPages()); } -SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document), m_pages() +SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document) { m_pages.reserve(m_document->numPages()); diff --git a/qt5/tests/test-password-qt5.cpp b/qt5/tests/test-password-qt5.cpp index 05b697d..f576457 100644 --- a/qt5/tests/test-password-qt5.cpp +++ b/qt5/tests/test-password-qt5.cpp @@ -125,7 +125,7 @@ int main(int argc, char **argv) test.setWindowTitle(QStringLiteral("Poppler-Qt5 Test")); test.show(); // show it - return a.exec(); // start event loop + return QApplication::exec(); // start event loop } #include "test-password-qt5.moc" diff --git a/qt5/tests/test-poppler-qt5.cpp b/qt5/tests/test-poppler-qt5.cpp index ed54281..e08a555 100644 --- a/qt5/tests/test-poppler-qt5.cpp +++ b/qt5/tests/test-poppler-qt5.cpp @@ -203,7 +203,7 @@ int main(int argc, char **argv) test.display(); test.show(); // show it - return a.exec(); // start event loop + return QApplication::exec(); // start event loop } else { Poppler::Page *page = doc->page(0); @@ -211,7 +211,7 @@ int main(int argc, char **argv) l->show(); delete page; delete doc; - return a.exec(); + return QApplication::exec(); } } diff --git a/qt6/demos/abstractinfodock.cpp b/qt6/demos/abstractinfodock.cpp index 8a2e216..91eeee4 100644 --- a/qt6/demos/abstractinfodock.cpp +++ b/qt6/demos/abstractinfodock.cpp @@ -23,7 +23,7 @@ AbstractInfoDock::AbstractInfoDock(QWidget *parent) : QDockWidget(parent), m_fil connect(this, &AbstractInfoDock::visibilityChanged, this, &AbstractInfoDock::slotVisibilityChanged); } -AbstractInfoDock::~AbstractInfoDock() { } +AbstractInfoDock::~AbstractInfoDock() = default; void AbstractInfoDock::documentLoaded() { diff --git a/qt6/demos/abstractinfodock.h b/qt6/demos/abstractinfodock.h index 9dd2576..6a04b2d 100644 --- a/qt6/demos/abstractinfodock.h +++ b/qt6/demos/abstractinfodock.h @@ -39,10 +39,9 @@ class AbstractInfoDock : public QDockWidget, public DocumentObserver protected: virtual void fillInfo() = 0; -private Q_SLOTS: +private: void slotVisibilityChanged(bool visible); -private: bool m_filled; }; diff --git a/qt6/demos/documentobserver.cpp b/qt6/demos/documentobserver.cpp index 9cf6b04..bdb4e6a 100644 --- a/qt6/demos/documentobserver.cpp +++ b/qt6/demos/documentobserver.cpp @@ -23,7 +23,7 @@ DocumentObserver::DocumentObserver() : m_viewer(nullptr) { } -DocumentObserver::~DocumentObserver() { } +DocumentObserver::~DocumentObserver() = default; Poppler::Document *DocumentObserver::document() const { diff --git a/qt6/demos/embeddedfiles.cpp b/qt6/demos/embeddedfiles.cpp index 22df1f6..c6f5fa0 100644 --- a/qt6/demos/embeddedfiles.cpp +++ b/qt6/demos/embeddedfiles.cpp @@ -32,7 +32,7 @@ EmbeddedFilesDock::EmbeddedFilesDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -EmbeddedFilesDock::~EmbeddedFilesDock() { } +EmbeddedFilesDock::~EmbeddedFilesDock() = default; void EmbeddedFilesDock::fillInfo() { diff --git a/qt6/demos/fonts.cpp b/qt6/demos/fonts.cpp index 9c282cf..c6f612e 100644 --- a/qt6/demos/fonts.cpp +++ b/qt6/demos/fonts.cpp @@ -37,7 +37,7 @@ FontsDock::FontsDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -FontsDock::~FontsDock() { } +FontsDock::~FontsDock() = default; void FontsDock::fillInfo() { diff --git a/qt6/demos/info.cpp b/qt6/demos/info.cpp index e00f5d8..c0c2a6f 100644 --- a/qt6/demos/info.cpp +++ b/qt6/demos/info.cpp @@ -32,7 +32,7 @@ InfoDock::InfoDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -InfoDock::~InfoDock() { } +InfoDock::~InfoDock() = default; void InfoDock::fillInfo() { diff --git a/qt6/demos/main_viewer.cpp b/qt6/demos/main_viewer.cpp index ad2f388..1428cad 100644 --- a/qt6/demos/main_viewer.cpp +++ b/qt6/demos/main_viewer.cpp @@ -29,5 +29,5 @@ int main(int argc, char *argv[]) if (args.count() > 1) { viewer->loadDocument(args.at(1)); } - return app.exec(); + return QApplication::exec(); } diff --git a/qt6/demos/metadata.cpp b/qt6/demos/metadata.cpp index 57460bf..49bff95 100644 --- a/qt6/demos/metadata.cpp +++ b/qt6/demos/metadata.cpp @@ -31,7 +31,7 @@ MetadataDock::MetadataDock(QWidget *parent) : AbstractInfoDock(parent) m_edit->setReadOnly(true); } -MetadataDock::~MetadataDock() { } +MetadataDock::~MetadataDock() = default; void MetadataDock::fillInfo() { diff --git a/qt6/demos/navigationtoolbar.cpp b/qt6/demos/navigationtoolbar.cpp index 00250a2..24b1241 100644 --- a/qt6/demos/navigationtoolbar.cpp +++ b/qt6/demos/navigationtoolbar.cpp @@ -28,13 +28,13 @@ NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) { - m_firstAct = addAction(tr("First"), this, SLOT(slotGoFirst())); - m_prevAct = addAction(tr("Previous"), this, SLOT(slotGoPrev())); + m_firstAct = addAction(tr("First"), this, &NavigationToolBar::slotGoFirst); + m_prevAct = addAction(tr("Previous"), this, &NavigationToolBar::slotGoPrev); m_pageCombo = new QComboBox(this); connect(m_pageCombo, &QComboBox::activated, this, &NavigationToolBar::slotComboActivated); addWidget(m_pageCombo); - m_nextAct = addAction(tr("Next"), this, SLOT(slotGoNext())); - m_lastAct = addAction(tr("Last"), this, SLOT(slotGoLast())); + m_nextAct = addAction(tr("Next"), this, &NavigationToolBar::slotGoNext); + m_lastAct = addAction(tr("Last"), this, &NavigationToolBar::slotGoLast); addSeparator(); @@ -68,7 +68,7 @@ NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) documentClosed(); } -NavigationToolBar::~NavigationToolBar() { } +NavigationToolBar::~NavigationToolBar() = default; void NavigationToolBar::documentLoaded() { diff --git a/qt6/demos/navigationtoolbar.h b/qt6/demos/navigationtoolbar.h index ffec8f4..be88950 100644 --- a/qt6/demos/navigationtoolbar.h +++ b/qt6/demos/navigationtoolbar.h @@ -44,7 +44,7 @@ class NavigationToolBar : public QToolBar, public DocumentObserver void zoomChanged(qreal value); // NOLINT(readability-inconsistent-declaration-parameter-name) void rotationChanged(int rotation); // NOLINT(readability-inconsistent-declaration-parameter-name) -private Q_SLOTS: +private: void slotGoFirst(); void slotGoPrev(); void slotGoNext(); @@ -53,7 +53,6 @@ private Q_SLOTS: void slotZoomComboActivated(int index); void slotRotationComboChanged(int idx); -private: QAction *m_firstAct; QAction *m_prevAct; QComboBox *m_pageCombo; diff --git a/qt6/demos/optcontent.cpp b/qt6/demos/optcontent.cpp index 39fc3e6..05b99ad 100644 --- a/qt6/demos/optcontent.cpp +++ b/qt6/demos/optcontent.cpp @@ -30,7 +30,7 @@ OptContentDock::OptContentDock(QWidget *parent) : AbstractInfoDock(parent) m_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -OptContentDock::~OptContentDock() { } +OptContentDock::~OptContentDock() = default; void OptContentDock::documentLoaded() { diff --git a/qt6/demos/optcontent.h b/qt6/demos/optcontent.h index 9da451b..a9893ac 100644 --- a/qt6/demos/optcontent.h +++ b/qt6/demos/optcontent.h @@ -38,10 +38,9 @@ class OptContentDock : public AbstractInfoDock protected: void fillInfo() override; -private Q_SLOTS: +private: void reloadImage(); -private: QTreeView *m_view; }; diff --git a/qt6/demos/pageview.cpp b/qt6/demos/pageview.cpp index 43f02d3..a9729d4 100644 --- a/qt6/demos/pageview.cpp +++ b/qt6/demos/pageview.cpp @@ -36,7 +36,7 @@ PageView::PageView(QWidget *parent) : QScrollArea(parent), m_zoom(1.0), m_rotati setWidget(m_imageLabel); } -PageView::~PageView() { } +PageView::~PageView() = default; void PageView::documentLoaded() { } diff --git a/qt6/demos/pageview.h b/qt6/demos/pageview.h index c7446d4..4984f81 100644 --- a/qt6/demos/pageview.h +++ b/qt6/demos/pageview.h @@ -39,7 +39,6 @@ class PageView : public QScrollArea, public DocumentObserver void documentClosed() override; void pageChanged(int page) override; -public Q_SLOTS: void slotZoomChanged(qreal value); void slotRotationChanged(int value); diff --git a/qt6/demos/permissions.cpp b/qt6/demos/permissions.cpp index 9c0ed93..3498738 100644 --- a/qt6/demos/permissions.cpp +++ b/qt6/demos/permissions.cpp @@ -30,7 +30,7 @@ PermissionsDock::PermissionsDock(QWidget *parent) : AbstractInfoDock(parent) m_table->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -PermissionsDock::~PermissionsDock() { } +PermissionsDock::~PermissionsDock() = default; void PermissionsDock::fillInfo() { diff --git a/qt6/demos/thumbnails.cpp b/qt6/demos/thumbnails.cpp index 065fd71..44a8ae8 100644 --- a/qt6/demos/thumbnails.cpp +++ b/qt6/demos/thumbnails.cpp @@ -38,7 +38,7 @@ ThumbnailsDock::ThumbnailsDock(QWidget *parent) : AbstractInfoDock(parent) connect(m_list, &QListWidget::itemActivated, this, &ThumbnailsDock::slotItemActivated); } -ThumbnailsDock::~ThumbnailsDock() { } +ThumbnailsDock::~ThumbnailsDock() = default; void ThumbnailsDock::fillInfo() { diff --git a/qt6/demos/thumbnails.h b/qt6/demos/thumbnails.h index a568df6..7e46d4a 100644 --- a/qt6/demos/thumbnails.h +++ b/qt6/demos/thumbnails.h @@ -39,10 +39,9 @@ class ThumbnailsDock : public AbstractInfoDock protected: void fillInfo() override; -private Q_SLOTS: +private: void slotItemActivated(QListWidgetItem *item); -private: QListWidget *m_list; }; diff --git a/qt6/demos/toc.cpp b/qt6/demos/toc.cpp index 491f26b..9bb8f40 100644 --- a/qt6/demos/toc.cpp +++ b/qt6/demos/toc.cpp @@ -127,7 +127,7 @@ TocDock::TocDock(QWidget *parent) : AbstractInfoDock(parent) m_tree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); } -TocDock::~TocDock() { } +TocDock::~TocDock() = default; void TocDock::expandItemModels(const QModelIndex &parent) { diff --git a/qt6/demos/viewer.h b/qt6/demos/viewer.h index bb1a990..82894de 100644 --- a/qt6/demos/viewer.h +++ b/qt6/demos/viewer.h @@ -47,7 +47,7 @@ class PdfViewer : public QMainWindow void loadDocument(const QString &file); void closeDocument(); -private Q_SLOTS: +private: void slotOpenFile(); void slotSaveCopy(); void slotAbout(); @@ -56,7 +56,6 @@ private Q_SLOTS: void slotToggleGfxAA(bool value); void slotRenderBackend(QAction *act); -private: void setPage(int page); int page() const; void xrefReconstructedHandler(); diff --git a/qt6/src/CMakeLists.txt b/qt6/src/CMakeLists.txt index 7104924..c0ee105 100644 --- a/qt6/src/CMakeLists.txt +++ b/qt6/src/CMakeLists.txt @@ -5,10 +5,6 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) - configure_file(poppler-version.h.in ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h @ONLY) set(poppler_qt6_SRCS @@ -38,7 +34,7 @@ set(poppler_qt6_SRCS ) add_library(poppler-qt6 ${poppler_qt6_SRCS}) generate_export_header(poppler-qt6 BASE_NAME poppler-qt6 EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h") -set_target_properties(poppler-qt6 PROPERTIES VERSION 3.2.0 SOVERSION 3) +set_target_properties(poppler-qt6 PROPERTIES VERSION 3.10.0 SOVERSION 3) if(MINGW AND BUILD_SHARED_LIBS) get_target_property(POPPLER_QT6_SOVERSION poppler-qt6 SOVERSION) set_target_properties(poppler-qt6 PROPERTIES SUFFIX "-${POPPLER_QT6_SOVERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}") @@ -49,10 +45,11 @@ if (ENABLE_NSS3) endif() if(USE_CMS) target_link_libraries(poppler-qt6 poppler ${LCMS2_LIBRARIES}) + target_include_directories(poppler-qt6 SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) endif() install(TARGETS poppler-qt6 RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(FILES +set(poppler_qt6_all_install_headers poppler-qt6.h poppler-link.h poppler-annotation.h @@ -60,7 +57,18 @@ install(FILES poppler-optcontent.h poppler-page-transition.h poppler-media.h + poppler-converter.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h - DESTINATION include/poppler/qt6) - +) +if (${CMAKE_VERSION} VERSION_LESS "3.23.0") + install(FILES ${poppler_qt6_all_install_headers} DESTINATION include/poppler/qt6) +else() + target_sources(poppler-qt6 + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR} + FILES ${poppler_qt6_all_install_headers} + ) + install(TARGETS poppler-qt6 FILE_SET HEADERS DESTINATION include/poppler/qt6) +endif() diff --git a/qt6/src/Doxyfile b/qt6/src/Doxyfile index 6f5ee6b..0426b8f 100644 --- a/qt6/src/Doxyfile +++ b/qt6/src/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = "Poppler Qt6" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 22.12.0 +PROJECT_NUMBER = 25.03.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. diff --git a/qt6/src/Mainpage.dox b/qt6/src/Mainpage.dox index 0d7f207..2b41168 100644 --- a/qt6/src/Mainpage.dox +++ b/qt6/src/Mainpage.dox @@ -33,16 +33,35 @@ simply need to add the following line to their C++ source files: #include @endcode + +For using the Qt6 interface on Android, there is an additional step - you must place the following font files in the assets/share/fonts directory of the Android APK: + + - NimbusMonoPS-Regular.otf + - NimbusMonoPS-Bold.otf + - NimbusMonoPS-BoldItalic.otf + - NimbusMonoPS-Italic.otf + - NimbusSans-Regular.otf + - NimbusSans-Bold.otf + - NimbusSans-BoldItalic.otf + - NimbusSans-Italic.otf + - StandardSymbolsPS.otf + - NimbusRoman-Bold.otf + - imbusRoman-BoldItalic.otf + - NimbusRoman-Italic.otf + - NimbusRoman-Regular.otf + - D050000L.otf + +These are used as substitute fonts for the base-14 fonts, and this step is required in order to reliably display documents with unembedded fonts. You can easily find these font files included within GhostScript. + A PDF document can then be loaded as follows: @code QString filename; -Poppler::Document* document = Poppler::Document::load(filename); +std::unique_ptr document = Poppler::Document::load(filename); if (!document || document->isLocked()) { // ... error message .... - delete document; return; } @endcode @@ -51,14 +70,14 @@ Pages can be rendered to QImages with the following commands: @code // Paranoid safety check -if (document == 0) { +if (!document) { // ... error message ... return; } // Access page of the PDF file -Poppler::Page* pdfPage = document->page(pageNumber); // Document starts at page 0 -if (pdfPage == 0) { +std::unique_ptr pdfPage = document->page(pageNumber); // Document starts at page 0 +if (!pdfPage) { // ... error message ... return; } @@ -73,7 +92,6 @@ if (image.isNull()) { // ... use image ... // after the usage, the page must be deleted -delete pdfPage; @endcode Finally, don't forget to destroy the document: diff --git a/qt6/src/QPainterOutputDev.cc b/qt6/src/QPainterOutputDev.cc index b017bab..c9c9d3a 100644 --- a/qt6/src/QPainterOutputDev.cc +++ b/qt6/src/QPainterOutputDev.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2005 Brad Hards -// Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2005-2009, 2011, 2012, 2014, 2015, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2008, 2010 Pino Toscano // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Petr Gajdos @@ -27,6 +27,7 @@ // Copyright (C) 2017, 2022 Adrian Johnson // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -162,10 +163,6 @@ QPainterOutputDev::QPainterOutputDev(QPainter *painter) : m_lastTransparencyGrou QPainterOutputDev::~QPainterOutputDev() { - for (auto &codeToGID : m_codeToGIDCache) { - gfree(const_cast(codeToGID.second)); - } - FT_Done_FreeType(m_ftLibrary); } @@ -174,10 +171,9 @@ void QPainterOutputDev::startDoc(PDFDoc *doc) xref = doc->getXRef(); m_doc = doc; - for (auto &codeToGID : m_codeToGIDCache) { - gfree(const_cast(codeToGID.second)); - } m_codeToGIDCache.clear(); + m_codeToGIDStack = {}; + m_codeToGID = nullptr; } void QPainterOutputDev::startPage(int pageNum, GfxState *state, XRef *) { } @@ -515,7 +511,7 @@ void QPainterOutputDev::updateFont(GfxState *state) if (codeToGIDIt != m_codeToGIDCache.end()) { - m_codeToGID = codeToGIDIt->second; + m_codeToGID = &codeToGIDIt->second; } else { @@ -547,16 +543,15 @@ void QPainterOutputDev::updateFont(GfxState *state) case fontType1C: case fontType1COT: { // Load the font face using FreeType - const int faceIndex = 0; // We always load the zero-th face from a font FT_Face freeTypeFace; if (fontLoc->locType != gfxFontLocEmbedded) { - if (ft_new_face_from_file(m_ftLibrary, fontLoc->path.c_str(), faceIndex, &freeTypeFace)) { + if (ft_new_face_from_file(m_ftLibrary, fontLoc->path.c_str(), fontLoc->fontNum, &freeTypeFace)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } } else { - if (FT_New_Memory_Face(m_ftLibrary, (const FT_Byte *)fontBuffer->data(), fontBuffer->size(), faceIndex, &freeTypeFace)) { + if (FT_New_Memory_Face(m_ftLibrary, (const FT_Byte *)fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum, &freeTypeFace)) { error(errSyntaxError, -1, "Couldn't create a FreeType face for '{0:s}'", gfxFont->getName() ? gfxFont->getName()->c_str() : "(unnamed)"); return; } @@ -564,9 +559,9 @@ void QPainterOutputDev::updateFont(GfxState *state) const char *name; - int *codeToGID = (int *)gmallocn(256, sizeof(int)); + std::vector codeToGID; + codeToGID.resize(256, 0); for (int i = 0; i < 256; ++i) { - codeToGID[i] = 0; if ((name = ((const char **)((Gfx8BitFont *)gfxFont.get())->getEncoding())[i])) { codeToGID[i] = (int)FT_Get_Name_Index(freeTypeFace, (char *)name); if (codeToGID[i] == 0) { @@ -580,74 +575,67 @@ void QPainterOutputDev::updateFont(GfxState *state) FT_Done_Face(freeTypeFace); - m_codeToGIDCache[id] = codeToGID; + m_codeToGIDCache[id] = std::move(codeToGID); break; } case fontTrueType: case fontTrueTypeOT: { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); - m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : nullptr; + m_codeToGIDCache[id] = (ff) ? ((Gfx8BitFont *)gfxFont.get())->getCodeToGIDMap(ff.get()) : std::vector {}; break; } case fontCIDType0: case fontCIDType0C: { - int *cidToGIDMap = nullptr; - int nCIDs = 0; + std::vector cidToGIDMap; // check for a CFF font if (!m_useCIDs) { auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? std::unique_ptr(FoFiType1C::load(fontLoc->path.c_str())) : std::unique_ptr(FoFiType1C::make(fontBuffer->data(), fontBuffer->size())); - cidToGIDMap = (ff) ? ff->getCIDToGIDMap(&nCIDs) : nullptr; + cidToGIDMap = (ff) ? ff->getCIDToGIDMap() : std::vector {}; } - m_codeToGIDCache[id] = cidToGIDMap; + m_codeToGIDCache[id] = std::move(cidToGIDMap); break; } case fontCIDType0COT: { - int *codeToGID = nullptr; + std::vector codeToGID; + ; - if (((GfxCIDFont *)gfxFont.get())->getCIDToGID()) { - int codeToGIDLen = ((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen(); - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont.get())->getCIDToGID(), codeToGIDLen * sizeof(int)); + if (((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCIDToGID(); } - int *cidToGIDMap = nullptr; - int nCIDs = 0; + std::vector cidToGIDMap; - if (!codeToGID && !m_useCIDs) { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + if (codeToGID.empty() && !m_useCIDs) { + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); if (ff && ff->isOpenTypeCFF()) { - cidToGIDMap = ff->getCIDToGIDMap(&nCIDs); + cidToGIDMap = ff->getCIDToGIDMap(); } } - m_codeToGIDCache[id] = codeToGID ? codeToGID : cidToGIDMap; + m_codeToGIDCache[id] = !codeToGID.empty() ? codeToGID : cidToGIDMap; break; } case fontCIDType2: case fontCIDType2OT: { - int *codeToGID = nullptr; - int codeToGIDLen = 0; - if (((GfxCIDFont *)gfxFont.get())->getCIDToGID()) { - codeToGIDLen = ((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen(); - if (codeToGIDLen) { - codeToGID = (int *)gmallocn(codeToGIDLen, sizeof(int)); - memcpy(codeToGID, ((GfxCIDFont *)gfxFont.get())->getCIDToGID(), codeToGIDLen * sizeof(int)); - } + std::vector codeToGID; + ; + if (((GfxCIDFont *)gfxFont.get())->getCIDToGIDLen() > 0) { + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCIDToGID(); } else { - auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str()) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size()); + auto ff = (fontLoc->locType != gfxFontLocEmbedded) ? FoFiTrueType::load(fontLoc->path.c_str(), fontLoc->fontNum) : FoFiTrueType::make(fontBuffer->data(), fontBuffer->size(), fontLoc->fontNum); if (!ff) { return; } - codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get(), &codeToGIDLen); + codeToGID = ((GfxCIDFont *)gfxFont.get())->getCodeToGIDMap(ff.get()); } m_codeToGIDCache[id] = codeToGID; @@ -659,7 +647,7 @@ void QPainterOutputDev::updateFont(GfxState *state) return; } - m_codeToGID = m_codeToGIDCache[id]; + m_codeToGID = &m_codeToGIDCache[id]; } } @@ -913,7 +901,7 @@ void QPainterOutputDev::drawChar(GfxState *state, double x, double y, double dx, } if (!(render & 1)) { - quint32 glyphIndex = (m_codeToGID) ? m_codeToGID[code] : code; + quint32 glyphIndex = (m_codeToGID && code < m_codeToGID->size()) ? m_codeToGID->at(code) : code; QPointF glyphPosition = QPointF(x - originX, y - originY); // QGlyphRun objects can hold an entire sequence of glyphs, and it would possibly @@ -980,7 +968,10 @@ void QPainterOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, 1, // numPixelComps 1 // getBits ); - imgStr->reset(); + if (!imgStr->reset()) { + imgStr->close(); + return; + } // TODO: Would using QImage::Format_Mono be more efficient here? QImage image(width, height, QImage::Format_ARGB32); @@ -1023,7 +1014,10 @@ void QPainterOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + imgStr->close(); + return; + } image = QImage(width, height, QImage::Format_ARGB32); data = reinterpret_cast(image.bits()); @@ -1080,10 +1074,14 @@ void QPainterOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream /* TODO: Do we want to cache these? */ auto imgStr = std::make_unique(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + return; + } auto maskImageStr = std::make_unique(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); - maskImageStr->reset(); + if (!maskImageStr->reset()) { + return; + } QImage image(width, height, QImage::Format_ARGB32); unsigned int *data = reinterpret_cast(image.bits()); diff --git a/qt6/src/QPainterOutputDev.h b/qt6/src/QPainterOutputDev.h index 3c133d6..cdac319 100644 --- a/qt6/src/QPainterOutputDev.h +++ b/qt6/src/QPainterOutputDev.h @@ -21,6 +21,7 @@ // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017, 2018, 2020 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -192,11 +193,11 @@ class QPainterOutputDev : public OutputDev using QPainterFontID = std::pair; std::map> m_rawFontCache; std::map> m_type3FontCache; - std::map m_codeToGIDCache; + std::map> m_codeToGIDCache; // The table that maps character codes to glyph indexes - const int *m_codeToGID; - std::stack m_codeToGIDStack; + const std::vector *m_codeToGID; + std::stack *> m_codeToGIDStack; FT_Library m_ftLibrary; // as of FT 2.1.8, CID fonts are indexed by CID instead of GID diff --git a/qt6/src/poppler-annotation-private.h b/qt6/src/poppler-annotation-private.h index 35e2676..bbbc398 100644 --- a/qt6/src/poppler-annotation-private.h +++ b/qt6/src/poppler-annotation-private.h @@ -6,6 +6,7 @@ * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Oliver Sander * Copyright (C) 2021, Mahmoud Ahmed Khalil + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -99,7 +100,7 @@ class AnnotationPrivate : public QSharedData void fillTransformationMTX(double MTX[6]) const; QRectF fromPdfRectangle(const PDFRectangle &r) const; PDFRectangle boundaryToPdfRectangle(const QRectF &r, int flags) const; - AnnotPath *toAnnotPath(const QVector &l) const; + std::unique_ptr toAnnotPath(const QVector &l) const; /* Scan page for annotations, parentId=0 searches for root annotations, subtypes empty means all subtypes */ static std::vector> findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentId = -1); diff --git a/qt6/src/poppler-annotation.cc b/qt6/src/poppler-annotation.cc index ed27a1f..4416dd6 100644 --- a/qt6/src/poppler-annotation.cc +++ b/qt6/src/poppler-annotation.cc @@ -1,5 +1,5 @@ /* poppler-annotation.cc: qt interface to poppler - * Copyright (C) 2006, 2009, 2012-2015, 2018-2022 Albert Astals Cid + * Copyright (C) 2006, 2009, 2012-2015, 2018-2022, 2024 Albert Astals Cid * Copyright (C) 2006, 2008, 2010 Pino Toscano * Copyright (C) 2012, Guillermo A. Amaral B. * Copyright (C) 2012-2014 Fabio D'Urso @@ -13,8 +13,9 @@ * Copyright (C) 2020-2022 Oliver Sander * Copyright (C) 2020 Katarina Behrens * Copyright (C) 2020 Thorsten Behrens - * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden + * Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021 Mahmoud Ahmed Khalil + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -47,6 +48,7 @@ #include "poppler-annotation-private.h" #include "poppler-page-private.h" #include "poppler-private.h" +#include "poppler-form.h" // poppler includes #include @@ -339,7 +341,7 @@ PDFRectangle AnnotationPrivate::boundaryToPdfRectangle(const QRectF &r, int rFla return Poppler::boundaryToPdfRectangle(pdfPage, r, rFlags); } -AnnotPath *AnnotationPrivate::toAnnotPath(const QVector &list) const +std::unique_ptr AnnotationPrivate::toAnnotPath(const QVector &list) const { const int count = list.size(); std::vector ac; @@ -354,7 +356,7 @@ AnnotPath *AnnotationPrivate::toAnnotPath(const QVector &list) const ac.emplace_back(x, y); } - return new AnnotPath(std::move(ac)); + return std::make_unique(std::move(ac)); } std::vector> AnnotationPrivate::findAnnotations(::Page *pdfPage, DocumentData *doc, const QSet &subtypes, int parentID) @@ -865,7 +867,7 @@ class Annotation::Style::Private : public QSharedData Annotation::Style::Style() : d(new Private) { } -Annotation::Style::Style(const Style &other) : d(other.d) { } +Annotation::Style::Style(const Style &other) = default; Annotation::Style &Annotation::Style::operator=(const Style &other) { @@ -876,7 +878,7 @@ Annotation::Style &Annotation::Style::operator=(const Style &other) return *this; } -Annotation::Style::~Style() { } +Annotation::Style::~Style() = default; QColor Annotation::Style::color() const { @@ -982,7 +984,7 @@ class Annotation::Popup::Private : public QSharedData Annotation::Popup::Popup() : d(new Private) { } -Annotation::Popup::Popup(const Popup &other) : d(other.d) { } +Annotation::Popup::Popup(const Popup &other) = default; Annotation::Popup &Annotation::Popup::operator=(const Popup &other) { @@ -993,7 +995,7 @@ Annotation::Popup &Annotation::Popup::operator=(const Popup &other) return *this; } -Annotation::Popup::~Popup() { } +Annotation::Popup::~Popup() = default; int Annotation::Popup::flags() const { @@ -1047,7 +1049,7 @@ void Annotation::Popup::setText(const QString &text) Annotation::Annotation(AnnotationPrivate &dd) : d_ptr(&dd) { } -Annotation::~Annotation() { } +Annotation::~Annotation() = default; QString Annotation::author() const { @@ -1156,9 +1158,8 @@ void Annotation::setModificationDate(const QDateTime &date) if (d->pdfAnnot) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); - GooString *s = timeToDateString(&t); - d->pdfAnnot->setModified(s); - delete s; + std::unique_ptr s = timeToDateString(&t); + d->pdfAnnot->setModified(std::move(s)); } else { d->pdfAnnot->setModified(nullptr); } @@ -1195,9 +1196,8 @@ void Annotation::setCreationDate(const QDateTime &date) if (markupann) { if (date.isValid()) { const time_t t = date.toSecsSinceEpoch(); - GooString *s = timeToDateString(&t); - markupann->setDate(s); - delete s; + std::unique_ptr s = timeToDateString(&t); + markupann->setDate(std::move(s)); } else { markupann->setDate(nullptr); } @@ -1311,7 +1311,7 @@ void Annotation::setBoundary(const QRectF &boundary) if (rect == d->pdfAnnot->getRect()) { return; } - d->pdfAnnot->setRect(&rect); + d->pdfAnnot->setRect(rect); } Annotation::Style Annotation::style() const @@ -1566,7 +1566,7 @@ void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationA // END Annotation implementation /** TextAnnotation [Annotation] */ -TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { } +TextAnnotationPrivate::TextAnnotationPrivate() : textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { } Annotation *TextAnnotationPrivate::makeAlias() { @@ -1656,7 +1656,7 @@ TextAnnotation::TextAnnotation(TextAnnotation::TextType type) : Annotation(*new TextAnnotation::TextAnnotation(TextAnnotationPrivate &dd) : Annotation(dd) { } -TextAnnotation::~TextAnnotation() { } +TextAnnotation::~TextAnnotation() = default; Annotation::SubType TextAnnotation::subType() const { @@ -1968,7 +1968,7 @@ class LineAnnotationPrivate : public AnnotationPrivate }; LineAnnotationPrivate::LineAnnotationPrivate() - : AnnotationPrivate(), lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) + : lineStartStyle(LineAnnotation::None), lineEndStyle(LineAnnotation::None), lineClosed(false), lineShowCaption(false), lineLeadingFwdPt(0), lineLeadingBackPt(0), lineIntent(LineAnnotation::Unknown) { } @@ -2019,7 +2019,7 @@ LineAnnotation::LineAnnotation(LineAnnotation::LineType type) : Annotation(*new LineAnnotation::LineAnnotation(LineAnnotationPrivate &dd) : Annotation(dd) { } -LineAnnotation::~LineAnnotation() { } +LineAnnotation::~LineAnnotation() = default; Annotation::SubType LineAnnotation::subType() const { @@ -2106,9 +2106,8 @@ void LineAnnotation::setLinePoints(const QVector &points) lineann->setVertices(x1, y1, x2, y2); } else { AnnotPolygon *polyann = static_cast(d->pdfAnnot); - AnnotPath *p = d->toAnnotPath(points); - polyann->setVertices(p); - delete p; + const std::unique_ptr p = d->toAnnotPath(points); + polyann->setVertices(*p); } } @@ -2419,7 +2418,7 @@ class GeomAnnotationPrivate : public AnnotationPrivate QColor geomInnerColor; }; -GeomAnnotationPrivate::GeomAnnotationPrivate() : AnnotationPrivate(), geomType(GeomAnnotation::InscribedSquare) { } +GeomAnnotationPrivate::GeomAnnotationPrivate() : geomType(GeomAnnotation::InscribedSquare) { } Annotation *GeomAnnotationPrivate::makeAlias() { @@ -2458,7 +2457,7 @@ GeomAnnotation::GeomAnnotation() : Annotation(*new GeomAnnotationPrivate()) { } GeomAnnotation::GeomAnnotation(GeomAnnotationPrivate &dd) : Annotation(dd) { } -GeomAnnotation::~GeomAnnotation() { } +GeomAnnotation::~GeomAnnotation() = default; Annotation::SubType GeomAnnotation::subType() const { @@ -2540,7 +2539,7 @@ class HighlightAnnotationPrivate : public AnnotationPrivate AnnotQuadrilaterals *toQuadrilaterals(const QList &quads) const; }; -HighlightAnnotationPrivate::HighlightAnnotationPrivate() : AnnotationPrivate(), highlightType(HighlightAnnotation::Highlight) { } +HighlightAnnotationPrivate::HighlightAnnotationPrivate() : highlightType(HighlightAnnotation::Highlight) { } Annotation *HighlightAnnotationPrivate::makeAlias() { @@ -2645,7 +2644,7 @@ HighlightAnnotation::HighlightAnnotation() : Annotation(*new HighlightAnnotation HighlightAnnotation::HighlightAnnotation(HighlightAnnotationPrivate &dd) : Annotation(dd) { } -HighlightAnnotation::~HighlightAnnotation() { } +HighlightAnnotation::~HighlightAnnotation() = default; Annotation::SubType HighlightAnnotation::subType() const { @@ -2709,7 +2708,7 @@ void HighlightAnnotation::setHighlightQuads(const QList(d->pdfAnnot); AnnotQuadrilaterals *quadrilaterals = d->toQuadrilaterals(quads); - hlann->setQuadrilaterals(quadrilaterals); + hlann->setQuadrilaterals(*quadrilaterals); delete quadrilaterals; } @@ -2728,7 +2727,7 @@ class StampAnnotationPrivate : public AnnotationPrivate QImage stampCustomImage; }; -StampAnnotationPrivate::StampAnnotationPrivate() : AnnotationPrivate(), stampIconName(QStringLiteral("Draft")) { } +StampAnnotationPrivate::StampAnnotationPrivate() : stampIconName(QStringLiteral("Draft")) { } Annotation *StampAnnotationPrivate::makeAlias() { @@ -2840,11 +2839,11 @@ AnnotStampImageHelper *StampAnnotationPrivate::convertQImageToAnnotStampImageHel AnnotStampImageHelper *annotImg; - if (sMaskData.count() > 0) { - AnnotStampImageHelper sMask(parentDoc->doc, width, height, ColorSpace::DeviceGray, 8, sMaskData.data(), sMaskData.count()); - annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count(), sMask.getRef()); + if (sMaskData.size() > 0) { + AnnotStampImageHelper sMask(parentDoc->doc, width, height, ColorSpace::DeviceGray, 8, sMaskData.data(), sMaskData.size()); + annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.size(), sMask.getRef()); } else { - annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.count()); + annotImg = new AnnotStampImageHelper(parentDoc->doc, width, height, colorSpace, bitsPerComponent, data.data(), data.size()); } return annotImg; @@ -2854,7 +2853,7 @@ StampAnnotation::StampAnnotation() : Annotation(*new StampAnnotationPrivate()) { StampAnnotation::StampAnnotation(StampAnnotationPrivate &dd) : Annotation(dd) { } -StampAnnotation::~StampAnnotation() { } +StampAnnotation::~StampAnnotation() = default; Annotation::SubType StampAnnotation::subType() const { @@ -2906,6 +2905,218 @@ void StampAnnotation::setStampCustomImage(const QImage &image) stampann->setCustomImage(annotCustomImage); } +/** SignatureAnnotation [Annotation] */ +class SignatureAnnotationPrivate : public AnnotationPrivate +{ +public: + SignatureAnnotationPrivate(); + Annotation *makeAlias() override; + Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override; + + // data fields + QString text; + QString leftText; + double fontSize = 10.0; + double leftFontSize = 20.0; + QColor fontColor = Qt::red; + QColor borderColor = Qt::red; + double borderWidth = 1.5; + QColor backgroundColor = QColor(240, 240, 240); + QString imagePath; + QString fieldPartialName; + std::unique_ptr<::FormFieldSignature> field = nullptr; +}; + +SignatureAnnotationPrivate::SignatureAnnotationPrivate() = default; + +Annotation *SignatureAnnotationPrivate::makeAlias() +{ + return new SignatureAnnotation(*this); +} + +Annot *SignatureAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc) +{ + SignatureAnnotation *q = static_cast(makeAlias()); + + // Set page and document + pdfPage = destPage; + parentDoc = doc; + + // Set pdfAnnot + PDFRectangle rect = boundaryToPdfRectangle(boundary, flags); + + std::unique_ptr gSignatureText = std::unique_ptr(QStringToUnicodeGooString(text)); + std::unique_ptr gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(leftText)); + + std::optional sig = destPage->getDoc()->createSignature(destPage, QStringToGooString(fieldPartialName), rect, *gSignatureText, *gSignatureLeftText, fontSize, leftFontSize, convertQColor(fontColor), borderWidth, + convertQColor(borderColor), convertQColor(backgroundColor), imagePath.toStdString()); + + if (!sig) { + return nullptr; + } + + sig->formWidget->updateWidgetAppearance(); + field = std::move(sig->field); + + // Set properties + flushBaseAnnotationProperties(); + + pdfAnnot = sig->annotWidget; + + pdfAnnot->incRefCnt(); + + delete q; + + return pdfAnnot; +} + +SignatureAnnotation::SignatureAnnotation() : Annotation(*new SignatureAnnotationPrivate()) { } + +SignatureAnnotation::SignatureAnnotation(SignatureAnnotationPrivate &dd) : Annotation(dd) { } + +SignatureAnnotation::~SignatureAnnotation() = default; + +Annotation::SubType SignatureAnnotation::subType() const +{ + return AWidget; +} + +void SignatureAnnotation::setText(const QString &text) +{ + Q_D(SignatureAnnotation); + d->text = text; +} + +void SignatureAnnotation::setLeftText(const QString &text) +{ + Q_D(SignatureAnnotation); + d->leftText = text; +} + +double SignatureAnnotation::fontSize() const +{ + Q_D(const SignatureAnnotation); + return d->fontSize; +} + +void SignatureAnnotation::setFontSize(double fontSize) +{ + Q_D(SignatureAnnotation); + d->fontSize = fontSize; +} + +double SignatureAnnotation::leftFontSize() const +{ + Q_D(const SignatureAnnotation); + return d->leftFontSize; +} + +void SignatureAnnotation::setLeftFontSize(double fontSize) +{ + Q_D(SignatureAnnotation); + d->leftFontSize = fontSize; +} + +QColor SignatureAnnotation::fontColor() const +{ + Q_D(const SignatureAnnotation); + return d->fontColor; +} + +void SignatureAnnotation::setFontColor(const QColor &color) +{ + Q_D(SignatureAnnotation); + d->fontColor = color; +} + +QColor SignatureAnnotation::borderColor() const +{ + Q_D(const SignatureAnnotation); + return d->borderColor; +} + +void SignatureAnnotation::setBorderColor(const QColor &color) +{ + Q_D(SignatureAnnotation); + d->borderColor = color; +} + +QColor SignatureAnnotation::backgroundColor() const +{ + Q_D(const SignatureAnnotation); + return d->backgroundColor; +} + +double SignatureAnnotation::borderWidth() const +{ + Q_D(const SignatureAnnotation); + return d->borderWidth; +} + +void SignatureAnnotation::setBorderWidth(double width) +{ + Q_D(SignatureAnnotation); + d->borderWidth = width; +} + +void SignatureAnnotation::setBackgroundColor(const QColor &color) +{ + Q_D(SignatureAnnotation); + d->backgroundColor = color; +} + +QString SignatureAnnotation::imagePath() const +{ + Q_D(const SignatureAnnotation); + return d->imagePath; +} + +void SignatureAnnotation::setImagePath(const QString &imagePath) +{ + Q_D(SignatureAnnotation); + d->imagePath = imagePath; +} + +QString SignatureAnnotation::fieldPartialName() const +{ + Q_D(const SignatureAnnotation); + return d->fieldPartialName; +} + +void SignatureAnnotation::setFieldPartialName(const QString &fieldPartialName) +{ + Q_D(SignatureAnnotation); + d->fieldPartialName = fieldPartialName; +} + +SignatureAnnotation::SigningResult SignatureAnnotation::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) +{ + Q_D(SignatureAnnotation); + auto formField = std::make_unique(d->parentDoc, d->pdfPage, static_cast<::FormWidgetSignature *>(d->field->getCreateWidget())); + + const auto result = formField->sign(outputFileName, data); + + switch (result) { + case FormFieldSignature::SigningSuccess: + return SigningSuccess; + case FormFieldSignature::FieldAlreadySigned: + return FieldAlreadySigned; + case FormFieldSignature::GenericSigningError: + return GenericSigningError; + case FormFieldSignature::InternalError: + return InternalError; + case FormFieldSignature::KeyMissing: + return KeyMissing; + case FormFieldSignature::WriteFailed: + return WriteFailed; + case FormFieldSignature::UserCancelled: + return UserCancelled; + case FormFieldSignature::BadPassphrase: + return BadPassphrase; + } + return GenericSigningError; +} + /** InkAnnotation [Annotation] */ class InkAnnotationPrivate : public AnnotationPrivate { @@ -2918,10 +3129,10 @@ class InkAnnotationPrivate : public AnnotationPrivate QList> inkPaths; // helper - AnnotPath **toAnnotPaths(const QList> &paths); + std::vector> toAnnotPaths(const QList> &paths); }; -InkAnnotationPrivate::InkAnnotationPrivate() : AnnotationPrivate() { } +InkAnnotationPrivate::InkAnnotationPrivate() = default; Annotation *InkAnnotationPrivate::makeAlias() { @@ -2929,12 +3140,12 @@ Annotation *InkAnnotationPrivate::makeAlias() } // Note: Caller is required to delete array elements and the array itself after use -AnnotPath **InkAnnotationPrivate::toAnnotPaths(const QList> &paths) +std::vector> InkAnnotationPrivate::toAnnotPaths(const QList> &paths) { - const int pathsNumber = paths.size(); - AnnotPath **res = new AnnotPath *[pathsNumber]; - for (int i = 0; i < pathsNumber; ++i) { - res[i] = toAnnotPath(paths[i]); + std::vector> res; + res.reserve(paths.size()); + for (const auto &path : paths) { + res.push_back(toAnnotPath(path)); } return res; } @@ -2967,7 +3178,7 @@ InkAnnotation::InkAnnotation() : Annotation(*new InkAnnotationPrivate()) { } InkAnnotation::InkAnnotation(InkAnnotationPrivate &dd) : Annotation(dd) { } -InkAnnotation::~InkAnnotation() { } +InkAnnotation::~InkAnnotation() = default; Annotation::SubType InkAnnotation::subType() const { @@ -2984,21 +3195,19 @@ QList> InkAnnotation::inkPaths() const const AnnotInk *inkann = static_cast(d->pdfAnnot); - const AnnotPath *const *paths = inkann->getInkList(); - if (!paths || !inkann->getInkListLength()) { + const std::vector> &paths = inkann->getInkList(); + if (paths.empty()) { return {}; } double MTX[6]; d->fillTransformationMTX(MTX); - const int pathsNumber = inkann->getInkListLength(); QList> inkPaths; - inkPaths.reserve(pathsNumber); - for (int m = 0; m < pathsNumber; ++m) { + inkPaths.reserve(paths.size()); + for (const auto &path : paths) { // transform each path in a list of normalized points .. QVector localList; - const AnnotPath *path = paths[m]; const int pointsNumber = path ? path->getCoordsLength() : 0; for (int n = 0; n < pointsNumber; ++n) { QPointF point; @@ -3008,6 +3217,7 @@ QList> InkAnnotation::inkPaths() const // ..and add it to the annotation inkPaths.append(localList); } + inkPaths.shrink_to_fit(); return inkPaths; } @@ -3021,14 +3231,8 @@ void InkAnnotation::setInkPaths(const QList> &paths) } AnnotInk *inkann = static_cast(d->pdfAnnot); - AnnotPath **annotpaths = d->toAnnotPaths(paths); - const int pathsNumber = paths.size(); - inkann->setInkList(annotpaths, pathsNumber); - - for (int i = 0; i < pathsNumber; ++i) { - delete annotpaths[i]; - } - delete[] annotpaths; + const std::vector> annotpaths = d->toAnnotPaths(paths); + inkann->setInkList(annotpaths); } /** LinkAnnotation [Annotation] */ @@ -3046,9 +3250,9 @@ class LinkAnnotationPrivate : public AnnotationPrivate QPointF linkRegion[4]; }; -LinkAnnotationPrivate::LinkAnnotationPrivate() : AnnotationPrivate(), linkHLMode(LinkAnnotation::Invert) { } +LinkAnnotationPrivate::LinkAnnotationPrivate() : linkHLMode(LinkAnnotation::Invert) { } -LinkAnnotationPrivate::~LinkAnnotationPrivate() { } +LinkAnnotationPrivate::~LinkAnnotationPrivate() = default; Annotation *LinkAnnotationPrivate::makeAlias() { @@ -3064,7 +3268,7 @@ LinkAnnotation::LinkAnnotation() : Annotation(*new LinkAnnotationPrivate()) { } LinkAnnotation::LinkAnnotation(LinkAnnotationPrivate &dd) : Annotation(dd) { } -LinkAnnotation::~LinkAnnotation() { } +LinkAnnotation::~LinkAnnotation() = default; Annotation::SubType LinkAnnotation::subType() const { @@ -3127,7 +3331,7 @@ class CaretAnnotationPrivate : public AnnotationPrivate CaretAnnotation::CaretSymbol symbol; }; -CaretAnnotationPrivate::CaretAnnotationPrivate() : AnnotationPrivate(), symbol(CaretAnnotation::None) { } +CaretAnnotationPrivate::CaretAnnotationPrivate() : symbol(CaretAnnotation::None) { } Annotation *CaretAnnotationPrivate::makeAlias() { @@ -3159,7 +3363,7 @@ CaretAnnotation::CaretAnnotation() : Annotation(*new CaretAnnotationPrivate()) { CaretAnnotation::CaretAnnotation(CaretAnnotationPrivate &dd) : Annotation(dd) { } -CaretAnnotation::~CaretAnnotation() { } +CaretAnnotation::~CaretAnnotation() = default; Annotation::SubType CaretAnnotation::subType() const { @@ -3205,7 +3409,7 @@ class FileAttachmentAnnotationPrivate : public AnnotationPrivate EmbeddedFile *embfile; }; -FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("PushPin")), embfile(nullptr) { } +FileAttachmentAnnotationPrivate::FileAttachmentAnnotationPrivate() : icon(QStringLiteral("PushPin")), embfile(nullptr) { } FileAttachmentAnnotationPrivate::~FileAttachmentAnnotationPrivate() { @@ -3226,7 +3430,7 @@ FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation(*new FileAttac FileAttachmentAnnotation::FileAttachmentAnnotation(FileAttachmentAnnotationPrivate &dd) : Annotation(dd) { } -FileAttachmentAnnotation::~FileAttachmentAnnotation() { } +FileAttachmentAnnotation::~FileAttachmentAnnotation() = default; Annotation::SubType FileAttachmentAnnotation::subType() const { @@ -3271,7 +3475,7 @@ class SoundAnnotationPrivate : public AnnotationPrivate SoundObject *sound; }; -SoundAnnotationPrivate::SoundAnnotationPrivate() : AnnotationPrivate(), icon(QStringLiteral("Speaker")), sound(nullptr) { } +SoundAnnotationPrivate::SoundAnnotationPrivate() : icon(QStringLiteral("Speaker")), sound(nullptr) { } SoundAnnotationPrivate::~SoundAnnotationPrivate() { @@ -3292,7 +3496,7 @@ SoundAnnotation::SoundAnnotation() : Annotation(*new SoundAnnotationPrivate()) { SoundAnnotation::SoundAnnotation(SoundAnnotationPrivate &dd) : Annotation(dd) { } -SoundAnnotation::~SoundAnnotation() { } +SoundAnnotation::~SoundAnnotation() = default; Annotation::SubType SoundAnnotation::subType() const { @@ -3337,7 +3541,7 @@ class MovieAnnotationPrivate : public AnnotationPrivate QString title; }; -MovieAnnotationPrivate::MovieAnnotationPrivate() : AnnotationPrivate(), movie(nullptr) { } +MovieAnnotationPrivate::MovieAnnotationPrivate() : movie(nullptr) { } MovieAnnotationPrivate::~MovieAnnotationPrivate() { @@ -3358,7 +3562,7 @@ MovieAnnotation::MovieAnnotation() : Annotation(*new MovieAnnotationPrivate()) { MovieAnnotation::MovieAnnotation(MovieAnnotationPrivate &dd) : Annotation(dd) { } -MovieAnnotation::~MovieAnnotation() { } +MovieAnnotation::~MovieAnnotation() = default; Annotation::SubType MovieAnnotation::subType() const { @@ -3403,7 +3607,7 @@ class ScreenAnnotationPrivate : public AnnotationPrivate QString title; }; -ScreenAnnotationPrivate::ScreenAnnotationPrivate() : AnnotationPrivate(), action(nullptr) { } +ScreenAnnotationPrivate::ScreenAnnotationPrivate() : action(nullptr) { } ScreenAnnotationPrivate::~ScreenAnnotationPrivate() { @@ -3424,7 +3628,7 @@ Annot *ScreenAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData ScreenAnnotation::ScreenAnnotation() : Annotation(*new ScreenAnnotationPrivate()) { } -ScreenAnnotation::~ScreenAnnotation() { } +ScreenAnnotation::~ScreenAnnotation() = default; Annotation::SubType ScreenAnnotation::subType() const { @@ -3483,7 +3687,7 @@ WidgetAnnotation::WidgetAnnotation(WidgetAnnotationPrivate &dd) : Annotation(dd) WidgetAnnotation::WidgetAnnotation() : Annotation(*new WidgetAnnotationPrivate()) { } -WidgetAnnotation::~WidgetAnnotation() { } +WidgetAnnotation::~WidgetAnnotation() = default; Annotation::SubType WidgetAnnotation::subType() const { @@ -3500,14 +3704,14 @@ std::unique_ptr WidgetAnnotation::additionalAction(AdditionalActionType ty class RichMediaAnnotation::Params::Private { public: - Private() { } + Private() = default; QString flashVars; }; RichMediaAnnotation::Params::Params() : d(new Private) { } -RichMediaAnnotation::Params::~Params() { } +RichMediaAnnotation::Params::~Params() = default; void RichMediaAnnotation::Params::setFlashVars(const QString &flashVars) { @@ -3535,7 +3739,7 @@ class RichMediaAnnotation::Instance::Private RichMediaAnnotation::Instance::Instance() : d(new Private) { } -RichMediaAnnotation::Instance::~Instance() { } +RichMediaAnnotation::Instance::~Instance() = default; void RichMediaAnnotation::Instance::setType(Type type) { @@ -3561,7 +3765,7 @@ RichMediaAnnotation::Params *RichMediaAnnotation::Instance::params() const class RichMediaAnnotation::Configuration::Private { public: - Private() { } + Private() = default; ~Private() { qDeleteAll(instances); @@ -3578,7 +3782,7 @@ class RichMediaAnnotation::Configuration::Private RichMediaAnnotation::Configuration::Configuration() : d(new Private) { } -RichMediaAnnotation::Configuration::~Configuration() { } +RichMediaAnnotation::Configuration::~Configuration() = default; void RichMediaAnnotation::Configuration::setType(Type type) { @@ -3629,7 +3833,7 @@ class RichMediaAnnotation::Asset::Private RichMediaAnnotation::Asset::Asset() : d(new Private) { } -RichMediaAnnotation::Asset::~Asset() { } +RichMediaAnnotation::Asset::~Asset() = default; void RichMediaAnnotation::Asset::setName(const QString &name) { @@ -3655,7 +3859,7 @@ EmbeddedFile *RichMediaAnnotation::Asset::embeddedFile() const class RichMediaAnnotation::Content::Private { public: - Private() { } + Private() = default; ~Private() { qDeleteAll(configurations); @@ -3674,7 +3878,7 @@ class RichMediaAnnotation::Content::Private RichMediaAnnotation::Content::Content() : d(new Private) { } -RichMediaAnnotation::Content::~Content() { } +RichMediaAnnotation::Content::~Content() = default; void RichMediaAnnotation::Content::setConfigurations(const QList &configurations) { @@ -3712,7 +3916,7 @@ class RichMediaAnnotation::Activation::Private RichMediaAnnotation::Activation::Activation() : d(new Private) { } -RichMediaAnnotation::Activation::~Activation() { } +RichMediaAnnotation::Activation::~Activation() = default; void RichMediaAnnotation::Activation::setCondition(Condition condition) { @@ -3734,7 +3938,7 @@ class RichMediaAnnotation::Deactivation::Private : public QSharedData RichMediaAnnotation::Deactivation::Deactivation() : d(new Private) { } -RichMediaAnnotation::Deactivation::~Deactivation() { } +RichMediaAnnotation::Deactivation::~Deactivation() = default; void RichMediaAnnotation::Deactivation::setCondition(Condition condition) { @@ -3757,7 +3961,7 @@ class RichMediaAnnotation::Settings::Private : public QSharedData RichMediaAnnotation::Settings::Settings() : d(new Private) { } -RichMediaAnnotation::Settings::~Settings() { } +RichMediaAnnotation::Settings::~Settings() = default; void RichMediaAnnotation::Settings::setActivation(RichMediaAnnotation::Activation *activation) { @@ -3812,7 +4016,7 @@ RichMediaAnnotation::RichMediaAnnotation() : Annotation(*new RichMediaAnnotation RichMediaAnnotation::RichMediaAnnotation(RichMediaAnnotationPrivate &dd) : Annotation(dd) { } -RichMediaAnnotation::~RichMediaAnnotation() { } +RichMediaAnnotation::~RichMediaAnnotation() = default; Annotation::SubType RichMediaAnnotation::subType() const { diff --git a/qt6/src/poppler-annotation.h b/qt6/src/poppler-annotation.h index 16f2eda..1c5004a 100644 --- a/qt6/src/poppler-annotation.h +++ b/qt6/src/poppler-annotation.h @@ -9,9 +9,10 @@ * Copyright (C) 2013, Anthony Granger * Copyright (C) 2018, Dileep Sankhla * Copyright (C) 2020, Katarina Behrens - * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden + * Copyright (C) 2020, 2024, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Oliver Sander * Copyright (C) 2021, Mahmoud Ahmed Khalil + * Copyright (C) 2024, 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -43,6 +44,7 @@ #include #include #include "poppler-export.h" +#include "poppler-converter.h" #include @@ -56,6 +58,7 @@ class LineAnnotationPrivate; class GeomAnnotationPrivate; class HighlightAnnotationPrivate; class StampAnnotationPrivate; +class SignatureAnnotationPrivate; class InkAnnotationPrivate; class LinkAnnotationPrivate; class CaretAnnotationPrivate; @@ -771,6 +774,95 @@ class POPPLER_QT6_EXPORT StampAnnotation : public Annotation Q_DISABLE_COPY(StampAnnotation) }; +/** + * \short Signature annotation. + * + * A signature annotation. By creating one and adding it to the page you can create + * an unsigned signature field. + * + * To read existing signature fields use FormFieldSignature. + * + * \since 24.10 + */ +class POPPLER_QT6_EXPORT SignatureAnnotation : public Annotation +{ + friend class AnnotationPrivate; + +public: + /** + * \since 24.10 + */ + enum SigningResult + { + SigningSuccess, ///< No error + FieldAlreadySigned, ///< Trying to sign a field that is already signed + GenericSigningError, ///< Unclassified error + InternalError, ///< Unexpected error, likely a bug in poppler \since 24.12 + KeyMissing, ///< Key not found (Either the input key is not from the list or the available keys has changed underneath) \since 24.12 + WriteFailed, ///< Write failed (permissions, faulty disk, ...) \since 24.12 + UserCancelled, ///< User cancelled the process \since 24.12 + BadPassphrase, ///< User entered bad passphrase \since 25.03 + }; + + SignatureAnnotation(); + ~SignatureAnnotation() override; + SubType subType() const override; + + void setText(const QString &text); + void setLeftText(const QString &text); + + /** + * Default: 10 + */ + double fontSize() const; + void setFontSize(double fontSize); + + /** + * Default: 20 + */ + double leftFontSize() const; + void setLeftFontSize(double fontSize); + + /** + * Default: red + */ + QColor fontColor() const; + void setFontColor(const QColor &color); + + /** + * Default: red + */ + QColor borderColor() const; + void setBorderColor(const QColor &color); + + /** + * border width in points + * + * Default: 1.5 + */ + double borderWidth() const; + void setBorderWidth(double width); + + /** + * Default: QColor(240, 240, 240) + */ + QColor backgroundColor() const; + void setBackgroundColor(const QColor &color); + + QString imagePath() const; + void setImagePath(const QString &imagePath); + + QString fieldPartialName() const; + void setFieldPartialName(const QString &fieldPartialName); + + [[nodiscard]] SigningResult sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data); + +private: + explicit SignatureAnnotation(SignatureAnnotationPrivate &dd); + Q_DECLARE_PRIVATE(SignatureAnnotation) + Q_DISABLE_COPY(SignatureAnnotation) +}; + /** * \short Ink Annotation. * diff --git a/qt6/src/poppler-base-converter.cc b/qt6/src/poppler-base-converter.cc index 2324c50..0e417c0 100644 --- a/qt6/src/poppler-base-converter.cc +++ b/qt6/src/poppler-base-converter.cc @@ -27,7 +27,7 @@ namespace Poppler { BaseConverterPrivate::BaseConverterPrivate() : document(nullptr), iodev(nullptr), ownIodev(true) { } -BaseConverterPrivate::~BaseConverterPrivate() { } +BaseConverterPrivate::~BaseConverterPrivate() = default; QIODevice *BaseConverterPrivate::openDevice() { diff --git a/qt6/src/poppler-converter.h b/qt6/src/poppler-converter.h new file mode 100644 index 0000000..e7394cd --- /dev/null +++ b/qt6/src/poppler-converter.h @@ -0,0 +1,465 @@ +/* poppler-converter.h: qt interface to poppler + * Copyright (C) 2005, Net Integration Technologies, Inc. + * Copyright (C) 2005, 2007, Brad Hards + * Copyright (C) 2005-2015, 2017-2022, Albert Astals Cid + * Copyright (C) 2005, Stefan Kebekus + * Copyright (C) 2006-2011, Pino Toscano + * Copyright (C) 2009 Shawn Rutledge + * Copyright (C) 2010 Suzuki Toshiya + * Copyright (C) 2010 Matthias Fauconneau + * Copyright (C) 2011 Andreas Hartmetz + * Copyright (C) 2011 Glad Deschrijver + * Copyright (C) 2012, Guillermo A. Amaral B. + * Copyright (C) 2012, Fabio D'Urso + * Copyright (C) 2012, Tobias Koenig + * Copyright (C) 2012, 2014, 2015, 2018, 2019 Adam Reichold + * Copyright (C) 2012, 2013 Thomas Freitag + * Copyright (C) 2013 Anthony Granger + * Copyright (C) 2016 Jakub Alba + * Copyright (C) 2017, 2020, 2021 Oliver Sander + * Copyright (C) 2017, 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich + * Copyright (C) 2018, 2021 Nelson Benítez León + * Copyright (C) 2019 Jan Grulich + * Copyright (C) 2019 Alexander Volkov + * Copyright (C) 2020 Philipp Knechtges + * Copyright (C) 2020 Katarina Behrens + * Copyright (C) 2020 Thorsten Behrens + * Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden + * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, . + * Copyright (C) 2021 Mahmoud Khalil + * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. + * Copyright (C) 2022 Martin + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __POPPLER_CONVERTER_H__ +#define __POPPLER_CONVERTER_H__ + +#include +#include + +#include "poppler-export.h" + +namespace Poppler { + +class DocumentData; + +class BaseConverterPrivate; +class PSConverterPrivate; +class PDFConverterPrivate; + +/** + \brief Base converter. + + This is the base class for the converters. +*/ +class POPPLER_QT6_EXPORT BaseConverter +{ + friend class Document; + +public: + /** + Destructor. + */ + virtual ~BaseConverter(); + + /** Sets the output file name. You must set this or the output device. */ + void setOutputFileName(const QString &outputFileName); + + /** + * Sets the output device. You must set this or the output file name. + */ + void setOutputDevice(QIODevice *device); + + /** + Does the conversion. + + \return whether the conversion succeeded + */ + virtual bool convert() = 0; + + enum Error + { + NoError, + FileLockedError, + OpenOutputError, + NotSupportedInputFileError + }; + + /** + Returns the last error + */ + Error lastError() const; + +protected: + /// \cond PRIVATE + explicit BaseConverter(BaseConverterPrivate &dd); + Q_DECLARE_PRIVATE(BaseConverter) + BaseConverterPrivate *d_ptr; + /// \endcond + +private: + Q_DISABLE_COPY(BaseConverter) +}; + +/** + Converts a PDF to PS + + Sizes have to be in Points (1/72 inch) + + If you are using QPrinter you can get paper size by doing: + \code +QPrinter dummy(QPrinter::PrinterResolution); +dummy.setFullPage(true); +dummy.setPageSize(myPageSize); +width = dummy.width(); +height = dummy.height(); + \endcode +*/ +class POPPLER_QT6_EXPORT PSConverter : public BaseConverter +{ + friend class Document; + +public: + /** + Options for the PS export. + */ + enum PSOption + { + Printing = 0x00000001, ///< The PS is generated for printing purposes + StrictMargins = 0x00000002, + ForceRasterization = 0x00000004, + PrintToEPS = 0x00000008, ///< Output EPS instead of PS + HideAnnotations = 0x00000010, ///< Don't print annotations + ForceOverprintPreview = 0x00000020 ///< Force rasterized overprint preview during conversion \since 23.09 + }; + Q_DECLARE_FLAGS(PSOptions, PSOption) + + /** + Destructor. + */ + ~PSConverter() override; + + /** Sets the list of pages to print. Mandatory. */ + void setPageList(const QList &pageList); + + /** + Sets the title of the PS Document. Optional + */ + void setTitle(const QString &title); + + /** + Sets the horizontal DPI. Defaults to 72.0 + */ + void setHDPI(double hDPI); + + /** + Sets the vertical DPI. Defaults to 72.0 + */ + void setVDPI(double vDPI); + + /** + Sets the rotate. Defaults to not rotated + */ + void setRotate(int rotate); + + /** + Sets the output paper width. Has to be set. + */ + void setPaperWidth(int paperWidth); + + /** + Sets the output paper height. Has to be set. + */ + void setPaperHeight(int paperHeight); + + /** + Sets the output right margin. Defaults to 0 + */ + void setRightMargin(int marginRight); + + /** + Sets the output bottom margin. Defaults to 0 + */ + void setBottomMargin(int marginBottom); + + /** + Sets the output left margin. Defaults to 0 + */ + void setLeftMargin(int marginLeft); + + /** + Sets the output top margin. Defaults to 0 + */ + void setTopMargin(int marginTop); + + /** + Defines if margins have to be strictly followed (even if that + means changing aspect ratio), or if the margins can be adapted + to keep aspect ratio. + + Defaults to false. + */ + void setStrictMargins(bool strictMargins); + + /** + Defines if the page will be rasterized to an image with overprint + preview enabled before printing. + + Defaults to false + + \since 23.09 + */ + void setForceOverprintPreview(bool forceOverprintPreview); + + /** Defines if the page will be rasterized to an image before printing. Defaults to false */ + void setForceRasterize(bool forceRasterize); + + /** + Sets the options for the PS export. + */ + void setPSOptions(PSOptions options); + + /** + The currently set options for the PS export. + + The default flags are: Printing. + */ + PSOptions psOptions() const; + + /** + Sets a function that will be called each time a page is converted. + + The payload belongs to the caller. + */ + void setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload); + + bool convert() override; + +private: + Q_DECLARE_PRIVATE(PSConverter) + Q_DISABLE_COPY(PSConverter) + + explicit PSConverter(DocumentData *document); +}; + +/** + Converts a PDF to PDF (thus saves a copy of the document). +*/ +class POPPLER_QT6_EXPORT PDFConverter : public BaseConverter +{ + friend class Document; + +public: + /** + Options for the PDF export. + */ + enum PDFOption + { + WithChanges = 0x00000001 ///< The changes done to the document are saved as well + }; + Q_DECLARE_FLAGS(PDFOptions, PDFOption) + + /** + Destructor. + */ + ~PDFConverter() override; + + /** + Sets the options for the PDF export. + */ + void setPDFOptions(PDFOptions options); + /** + The currently set options for the PDF export. + */ + PDFOptions pdfOptions() const; + + /** + * Holds data for a new signature + * - Common Name of cert to sign (aka nickname) + * - password for the cert + * - page where to add the signature + * - rect for the signature annotation + * - text that will be shown inside the rect + * - font size and color + * - border width and color + * - background color + * \since 21.01 + */ + class POPPLER_QT6_EXPORT NewSignatureData + { + public: + NewSignatureData(); + ~NewSignatureData(); + NewSignatureData(const NewSignatureData &) = delete; + NewSignatureData &operator=(const NewSignatureData &) = delete; + + QString certNickname() const; + void setCertNickname(const QString &certNickname); + + QString password() const; + void setPassword(const QString &password); + + int page() const; + void setPage(int page); + + QRectF boundingRectangle() const; + void setBoundingRectangle(const QRectF &rect); + + QString signatureText() const; + void setSignatureText(const QString &text); + + /** + * If this text is not empty, the signature representation + * will split in two, with this text on the left and signatureText + * on the right + * + * \since 21.06 + */ + QString signatureLeftText() const; + void setSignatureLeftText(const QString &text); + + /** + * Signature's property Reason. + * + * Default: an empty string. + * + * \since 21.10 + */ + QString reason() const; + void setReason(const QString &reason); + + /** + * Signature's property Location. + * + * Default: an empty string. + * + * \since 21.10 + */ + QString location() const; + void setLocation(const QString &location); + + /** + * Default: 10 + */ + double fontSize() const; + void setFontSize(double fontSize); + + /** + * Default: 20 + * + * \since 21.06 + */ + double leftFontSize() const; + void setLeftFontSize(double fontSize); + + /** + * Default: red + */ + QColor fontColor() const; + void setFontColor(const QColor &color); + + /** + * Default: red + */ + QColor borderColor() const; + void setBorderColor(const QColor &color); + + /** + * border width in points + * + * Default: 1.5 + * + * \since 21.05 + */ + double borderWidth() const; + void setBorderWidth(double width); + + /** + * Default: QColor(240, 240, 240) + */ + QColor backgroundColor() const; + void setBackgroundColor(const QColor &color); + + /** + * Default: QUuid::createUuid().toString() + */ + QString fieldPartialName() const; + void setFieldPartialName(const QString &name); + + /** + * Document owner password (needed if the document that is being signed is password protected) + * + * Default: no password + * + * \since 22.02 + */ + QByteArray documentOwnerPassword() const; + void setDocumentOwnerPassword(const QByteArray &password); + + /** + * Document user password (needed if the document that is being signed is password protected) + * + * Default: no password + * + * \since 22.02 + */ + QByteArray documentUserPassword() const; + void setDocumentUserPassword(const QByteArray &password); + + /** + * Filesystem path to an image file to be used as background + * image for the signature annotation widget. + * + * Default: empty + * + * \since 22.02 + */ + QString imagePath() const; + void setImagePath(const QString &path); + + private: + struct NewSignatureDataPrivate; + NewSignatureDataPrivate *const d; + }; + + /** + Sign PDF at given Annotation / signature form + + \param data new signature data + + \return whether the signing succeeded + + \since 21.01 + */ + bool sign(const NewSignatureData &data); + + bool convert() override; + +private: + Q_DECLARE_PRIVATE(PDFConverter) + Q_DISABLE_COPY(PDFConverter) + + explicit PDFConverter(DocumentData *document); +}; + +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PDFConverter::PDFOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PSConverter::PSOptions) + +#endif diff --git a/qt6/src/poppler-document.cc b/qt6/src/poppler-document.cc index 57c70e5..9eef7d8 100644 --- a/qt6/src/poppler-document.cc +++ b/qt6/src/poppler-document.cc @@ -20,6 +20,9 @@ * Copyright (C) 2020 Thorsten Behrens * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 Stefan Brüns + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,9 +56,11 @@ #include #include #include +#include #include "poppler-form.h" #include "poppler-private.h" +#include "poppler-link-private.h" #include "poppler-page-private.h" #include "poppler-outline-private.h" @@ -243,9 +248,8 @@ QByteArray Document::fontData(const FontInfo &fi) const Object refObj(fi.m_data->embRef); Object strObj = refObj.fetch(xref); - if (strObj.isStream()) { + if (strObj.isStream() && strObj.streamReset()) { int c; - strObj.streamReset(); while ((c = strObj.streamGetChar()) != EOF) { result.append((char)c); } @@ -272,8 +276,8 @@ bool Document::setInfo(const QString &key, const QString &val) return false; } - GooString *goo = QStringToUnicodeGooString(val); - m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), goo); + std::unique_ptr goo = QStringToUnicodeGooString(val); + m_doc->doc->setDocInfoStringEntry(key.toLatin1().constData(), std::move(goo)); return true; } @@ -594,10 +598,9 @@ QVector Document::outline() const std::unique_ptr Document::linkDestination(const QString &name) { - GooString *namedDest = QStringToGooString(name); - LinkDestinationData ldd(nullptr, namedDest, m_doc, false); + const std::unique_ptr namedDest = QStringToGooString(name); + LinkDestinationData ldd(nullptr, namedDest.get(), m_doc, false); auto ld = std::make_unique(ldd); - delete namedDest; return ld; } @@ -741,21 +744,68 @@ OptContentModel *Document::optionalContentModel() return (OptContentModel *)m_doc->m_optContentModel; } +void Document::applyResetFormsLink(const LinkResetForm &link) +{ + const LinkResetFormPrivate *lrfp = link.d_func(); + Catalog *catalog = m_doc->doc->getCatalog(); + if (catalog && catalog->isOk()) { + Form *form = catalog->getForm(); + if (form) { + std::vector stdStringFields; + const QStringList fields = lrfp->m_fields; + stdStringFields.reserve(fields.size()); + for (const auto &field : fields) { + stdStringFields.emplace_back(field.toStdString()); + } + form->reset(stdStringFields, lrfp->m_exclude); + } + } +} + QStringList Document::scripts() const { Catalog *catalog = m_doc->doc->getCatalog(); const int numScripts = catalog->numJS(); QStringList scripts; for (int i = 0; i < numScripts; ++i) { - GooString *s = catalog->getJS(i); - if (s) { + std::string s = catalog->getJS(i); + if (!s.empty()) { scripts.append(UnicodeParsedString(s)); - delete s; } } return scripts; } +std::unique_ptr Document::additionalAction(DocumentAdditionalActionsType type) const +{ + Catalog::DocumentAdditionalActionsType actionType; + switch (type) { + case CloseDocument: + actionType = Catalog::actionCloseDocument; + break; + case SaveDocumentStart: + actionType = Catalog::actionSaveDocumentStart; + break; + case SaveDocumentFinish: + actionType = Catalog::actionSaveDocumentFinish; + break; + case PrintDocumentStart: + actionType = Catalog::actionPrintDocumentStart; + break; + case PrintDocumentFinish: + actionType = Catalog::actionPrintDocumentFinish; + break; + default: + return {}; + } + + if (std::unique_ptr<::LinkAction> act = m_doc->doc->getCatalog()->getAdditionalAction(actionType)) { + return PageData::convertLinkActionToLink(act.get(), m_doc, QRectF()); + } + + return {}; +} + bool Document::getPdfId(QByteArray *permanentId, QByteArray *updateId) const { GooString gooPermanentId; @@ -843,22 +893,22 @@ QDateTime convertDate(const char *dateString) QDate d(year, mon, day); QTime t(hour, min, sec); if (d.isValid() && t.isValid()) { - QDateTime dt(d, t, Qt::UTC); + int tzSecs = 0; if (tz) { // then we have some form of timezone if ('Z' == tz) { // We are already at UTC } else if ('+' == tz) { // local time is ahead of UTC - dt = dt.addSecs(-1 * ((tzHours * 60) + tzMins) * 60); + tzSecs = (tzHours * 3600) + (tzMins * 60); } else if ('-' == tz) { // local time is behind UTC - dt = dt.addSecs(((tzHours * 60) + tzMins) * 60); + tzSecs = (tzHours * -3600) + (tzMins * -60); } else { qWarning("unexpected tz val"); } } - return dt; + return QDateTime(d, t, QTimeZone(tzSecs)); } } return QDateTime(); diff --git a/qt6/src/poppler-embeddedfile.cc b/qt6/src/poppler-embeddedfile.cc index 1b8a7a1..6bfe19b 100644 --- a/qt6/src/poppler-embeddedfile.cc +++ b/qt6/src/poppler-embeddedfile.cc @@ -3,6 +3,7 @@ * Copyright (C) 2005, Brad Hards * Copyright (C) 2008, 2011, Pino Toscano * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich + * Copyright (C) 2023, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,19 +104,11 @@ QByteArray EmbeddedFile::data() return QByteArray(); } - stream->reset(); - int dataLen = 0; - QByteArray fileArray; - int i; - while ((i = stream->getChar()) != EOF) { - if (dataLen >= fileArray.size()) { - fileArray.resize(dataLen + 32768); - } - fileArray[dataLen] = (char)i; - ++dataLen; + if (!stream->reset()) { + return QByteArray(); } - fileArray.resize(dataLen); - return fileArray; + auto data = stream->toUnsignedChars(); + return QByteArray(reinterpret_cast(data.data()), data.size()); } bool EmbeddedFile::isValid() const diff --git a/qt6/src/poppler-form.cc b/qt6/src/poppler-form.cc index 6f264bd..8684d4c 100644 --- a/qt6/src/poppler-form.cc +++ b/qt6/src/poppler-form.cc @@ -1,6 +1,6 @@ /* poppler-form.h: qt interface to poppler * Copyright (C) 2007-2008, 2011, Pino Toscano - * Copyright (C) 2008, 2011, 2012, 2015-2022 Albert Astals Cid + * Copyright (C) 2008, 2011, 2012, 2015-2024 Albert Astals Cid * Copyright (C) 2011 Carlos Garcia Campos * Copyright (C) 2012, Adam Reichold * Copyright (C) 2016, Hanno Meyer-Thurow @@ -16,6 +16,9 @@ * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2021 Theofilos Intzoglou * Copyright (C) 2022 Alexander Sulfrian + * Copyright (C) 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 Stefan Brüns * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +40,7 @@ #include #include +#include #include #include @@ -44,8 +48,12 @@ #include #include #include +#include #ifdef ENABLE_NSS3 -# include +# include +#endif +#ifdef ENABLE_GPGME +# include #endif #include "poppler-page-private.h" @@ -55,10 +63,6 @@ #include #include -#ifdef ENABLE_NSS3 -# include -#endif - namespace { Qt::Alignment formTextAlignment(::FormWidget *fm) @@ -159,15 +163,14 @@ QString FormField::name() const void FormField::setName(const QString &name) const { - GooString *goo = QStringToGooString(name); + const std::unique_ptr goo = QStringToGooString(name); m_formData->fm->setPartialName(*goo); - delete goo; } QString FormField::fullyQualifiedName() const { QString name; - if (GooString *goo = m_formData->fm->getFullyQualifiedName()) { + if (const GooString *goo = m_formData->fm->getFullyQualifiedName()) { name = UnicodeParsedString(goo); } return name; @@ -284,7 +287,7 @@ std::unique_ptr FormField::additionalAction(Annotation::AdditionalActionTy FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldButton::~FormFieldButton() { } +FormFieldButton::~FormFieldButton() = default; FormFieldButton::FormType FormFieldButton::type() const { @@ -393,7 +396,7 @@ QList FormFieldButton::siblings() const FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldText::~FormFieldText() { } +FormFieldText::~FormFieldText() = default; FormField::FormType FormFieldText::type() const { @@ -420,17 +423,15 @@ QString FormFieldText::text() const void FormFieldText::setText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); - GooString *goo = QStringToUnicodeGooString(text); - fwt->setContent(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwt->setContent(std::move(goo)); } void FormFieldText::setAppearanceText(const QString &text) { FormWidgetText *fwt = static_cast(m_formData->fm); - GooString *goo = QStringToUnicodeGooString(text); - fwt->setAppearanceContent(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwt->setAppearanceContent(std::move(goo)); } bool FormFieldText::isPassword() const @@ -477,7 +478,7 @@ void FormFieldText::setFontSize(int fontSize) FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldChoice::~FormFieldChoice() { } +FormFieldChoice::~FormFieldChoice() = default; FormFieldChoice::FormType FormFieldChoice::type() const { @@ -570,9 +571,8 @@ void FormFieldChoice::setEditChoice(const QString &text) FormWidgetChoice *fwc = static_cast(m_formData->fm); if (fwc->isCombo() && fwc->hasEdit()) { - GooString *goo = QStringToUnicodeGooString(text); - fwc->setEditChoice(goo); - delete goo; + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwc->setEditChoice(std::move(goo)); } } @@ -587,6 +587,13 @@ bool FormFieldChoice::canBeSpellChecked() const return !fwc->noSpellCheck(); } +void FormFieldChoice::setAppearanceChoiceText(const QString &text) +{ + FormWidgetChoice *fwc = static_cast(m_formData->fm); + std::unique_ptr goo = QStringToUnicodeGooString(text); + fwc->setAppearanceChoiceContent(std::move(goo)); +} + class CertificateInfoPrivate { public: @@ -612,6 +619,9 @@ class CertificateInfoPrivate int version; bool is_self_signed; bool is_null; + bool is_qualified; + CertificateInfo::CertificateType certificateType; + CertificateInfo::KeyLocation keyLocation; }; CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) @@ -621,7 +631,7 @@ CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { } -CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { } +CertificateInfo::CertificateInfo(const CertificateInfo &other) = default; CertificateInfo::~CertificateInfo() = default; @@ -652,6 +662,18 @@ QByteArray CertificateInfo::serialNumber() const return d->serial_number; } +bool CertificateInfo::isQualified() const +{ + Q_D(const CertificateInfo); + return d->is_qualified; +} + +CertificateInfo::CertificateType CertificateInfo::certificateType() const +{ + Q_D(const CertificateInfo); + return d->certificateType; +} + QString CertificateInfo::issuerInfo(EntityInfoKey key) const { Q_D(const CertificateInfo); @@ -737,6 +759,12 @@ CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const return kuExtensions; } +CertificateInfo::KeyLocation CertificateInfo::keyLocation() const +{ + Q_D(const CertificateInfo); + return d->keyLocation; +} + QByteArray CertificateInfo::publicKey() const { Q_D(const CertificateInfo); @@ -778,14 +806,18 @@ QByteArray CertificateInfo::certificateData() const bool CertificateInfo::checkPassword(const QString &password) const { -#ifdef ENABLE_NSS3 +#ifdef ENABLE_SIGNATURES + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + return false; + } Q_D(const CertificateInfo); - SignatureHandler sigHandler(d->nick_name.toUtf8().constData(), SEC_OID_SHA256); + auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256); unsigned char buffer[5]; memcpy(buffer, "test", 5); - sigHandler.updateHash(buffer, 5); - std::unique_ptr tmpSignature = sigHandler.signDetached(password.toUtf8().constData()); - return tmpSignature.get() != nullptr; + sigHandler->addData(buffer, 5); + std::variant, CryptoSign::SigningError> tmpSignature = sigHandler->signDetached(password.toStdString()); + return std::holds_alternative>(tmpSignature); #else return false; #endif @@ -805,7 +837,7 @@ class SignatureValidationInfoPrivate QString signer_subject_dn; QString location; QString reason; - int hash_algorithm; + HashAlgorithm hash_algorithm; time_t signing_time; QList range_bounds; qint64 docLength; @@ -813,9 +845,9 @@ class SignatureValidationInfoPrivate SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { } -SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { } +SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) = default; -SignatureValidationInfo::~SignatureValidationInfo() { } +SignatureValidationInfo::~SignatureValidationInfo() = default; SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const { @@ -855,24 +887,26 @@ QString SignatureValidationInfo::reason() const SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const { -#ifdef ENABLE_NSS3 +#ifdef ENABLE_SIGNATURES Q_D(const SignatureValidationInfo); switch (d->hash_algorithm) { - case HASH_AlgMD2: + case ::HashAlgorithm::Md2: return HashAlgorithmMd2; - case HASH_AlgMD5: + case ::HashAlgorithm::Md5: return HashAlgorithmMd5; - case HASH_AlgSHA1: + case ::HashAlgorithm::Sha1: return HashAlgorithmSha1; - case HASH_AlgSHA256: + case ::HashAlgorithm::Sha256: return HashAlgorithmSha256; - case HASH_AlgSHA384: + case ::HashAlgorithm::Sha384: return HashAlgorithmSha384; - case HASH_AlgSHA512: + case ::HashAlgorithm::Sha512: return HashAlgorithmSha512; - case HASH_AlgSHA224: + case ::HashAlgorithm::Sha224: return HashAlgorithmSha224; + case ::HashAlgorithm::Unknown: + return HashAlgorithmUnknown; } #endif return HashAlgorithmUnknown; @@ -931,7 +965,7 @@ SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValid FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique(doc, p, w)) { } -FormFieldSignature::~FormFieldSignature() { } +FormFieldSignature::~FormFieldSignature() = default; FormField::FormType FormFieldSignature::type() const { @@ -943,19 +977,22 @@ FormFieldSignature::SignatureType FormFieldSignature::signatureType() const SignatureType sigType = AdbePkcs7detached; FormWidgetSignature *fws = static_cast(m_formData->fm); switch (fws->signatureType()) { - case adbe_pkcs7_sha1: + case CryptoSign::SignatureType::adbe_pkcs7_sha1: sigType = AdbePkcs7sha1; break; - case adbe_pkcs7_detached: + case CryptoSign::SignatureType::adbe_pkcs7_detached: sigType = AdbePkcs7detached; break; - case ETSI_CAdES_detached: + case CryptoSign::SignatureType::ETSI_CAdES_detached: sigType = EtsiCAdESdetached; break; - case unknown_signature_type: + case CryptoSign::SignatureType::unknown_signature_type: sigType = UnknownSignatureType; break; - case unsigned_signature_field: + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + sigType = G10cPgpSignatureDetached; + break; + case CryptoSign::SignatureType::unsigned_signature_field: sigType = UnsignedSignature; break; } @@ -964,7 +1001,35 @@ FormFieldSignature::SignatureType FormFieldSignature::signatureType() const SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const { - return validate(opt, QDateTime()); + auto tempResult = validateAsync(opt); + tempResult.first.d_ptr->certificate_status = validateResult(); + return tempResult.first; +} + +static CertificateInfo::CertificateType fromPopplerCore(CertificateType type) +{ + switch (type) { + case CertificateType::PGP: + return CertificateInfo::CertificateType::PGP; + case CertificateType::X509: + return CertificateInfo::CertificateType::X509; + } + return CertificateInfo::CertificateType::X509; // fallback +} + +static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location) +{ + switch (location) { + case KeyLocation::Computer: + return CertificateInfo::KeyLocation::Computer; + case KeyLocation::Other: + return CertificateInfo::KeyLocation::Other; + case KeyLocation::Unknown: + return CertificateInfo::KeyLocation::Unknown; + case KeyLocation::HardwareToken: + return CertificateInfo::KeyLocation::HardwareToken; + } + return CertificateInfo::KeyLocation::Unknown; } static CertificateInfoPrivate *createCertificateInfoPrivate(const X509CertificateInfo *ci) @@ -974,6 +1039,8 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat if (ci) { certPriv->version = ci->getVersion(); certPriv->ku_extensions = ci->getKeyUsageExtensions(); + certPriv->keyLocation = fromPopplerCore(ci->getKeyLocation()); + certPriv->certificateType = fromPopplerCore(ci->getCertificateType()); const GooString &certSerial = ci->getSerialNumber(); certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength()); @@ -993,8 +1060,8 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat certPriv->nick_name = ci->getNickName().c_str(); X509CertificateInfo::Validity certValidity = ci->getValidity(); - certPriv->validity_start = QDateTime::fromSecsSinceEpoch(certValidity.notBefore, Qt::UTC); - certPriv->validity_end = QDateTime::fromSecsSinceEpoch(certValidity.notAfter, Qt::UTC); + certPriv->validity_start = QDateTime::fromSecsSinceEpoch(certValidity.notBefore, QTimeZone::utc()); + certPriv->validity_end = QDateTime::fromSecsSinceEpoch(certValidity.notAfter, QTimeZone::utc()); const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo(); certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength()); @@ -1005,17 +1072,35 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat certPriv->certificate_der = QByteArray(certDer.c_str(), certDer.getLength()); certPriv->is_null = false; + certPriv->is_qualified = ci->isQualified(); } return certPriv; } -SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const +static SignatureValidationInfo::CertificateStatus fromInternal(CertificateValidationStatus status) { - FormWidgetSignature *fws = static_cast(m_formData->fm); - const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1; - SignatureInfo *si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch); + switch (status) { + case CERTIFICATE_TRUSTED: + return SignatureValidationInfo::CertificateTrusted; + case CERTIFICATE_UNTRUSTED_ISSUER: + return SignatureValidationInfo::CertificateUntrustedIssuer; + case CERTIFICATE_UNKNOWN_ISSUER: + return SignatureValidationInfo::CertificateUnknownIssuer; + case CERTIFICATE_REVOKED: + return SignatureValidationInfo::CertificateRevoked; + case CERTIFICATE_EXPIRED: + return SignatureValidationInfo::CertificateExpired; + default: + case CERTIFICATE_GENERIC_ERROR: + return SignatureValidationInfo::CertificateGenericError; + case CERTIFICATE_NOT_VERIFIED: + return SignatureValidationInfo::CertificateNotVerified; + } +} +static SignatureValidationInfo fromInternal(SignatureInfo *si, FormWidgetSignature *fws) +{ // get certificate info const X509CertificateInfo *ci = si->getCertificateInfo(); CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci); @@ -1045,32 +1130,9 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v priv->signature_status = SignatureValidationInfo::SignatureNotVerified; break; } - switch (si->getCertificateValStatus()) { - case CERTIFICATE_TRUSTED: - priv->certificate_status = SignatureValidationInfo::CertificateTrusted; - break; - case CERTIFICATE_UNTRUSTED_ISSUER: - priv->certificate_status = SignatureValidationInfo::CertificateUntrustedIssuer; - break; - case CERTIFICATE_UNKNOWN_ISSUER: - priv->certificate_status = SignatureValidationInfo::CertificateUnknownIssuer; - break; - case CERTIFICATE_REVOKED: - priv->certificate_status = SignatureValidationInfo::CertificateRevoked; - break; - case CERTIFICATE_EXPIRED: - priv->certificate_status = SignatureValidationInfo::CertificateExpired; - break; - default: - case CERTIFICATE_GENERIC_ERROR: - priv->certificate_status = SignatureValidationInfo::CertificateGenericError; - break; - case CERTIFICATE_NOT_VERIFIED: - priv->certificate_status = SignatureValidationInfo::CertificateNotVerified; - break; - } - priv->signer_name = si->getSignerName(); - priv->signer_subject_dn = si->getSubjectDN(); + priv->certificate_status = SignatureValidationInfo::CertificateVerificationInProgress; + priv->signer_name = QString::fromStdString(si->getSignerName()); + priv->signer_subject_dn = QString::fromStdString(si->getSubjectDN()); priv->hash_algorithm = si->getHashAlgorithm(); priv->location = UnicodeParsedString(si->getLocation().toStr()); priv->reason = UnicodeParsedString(si->getReason().toStr()); @@ -1090,10 +1152,54 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v return SignatureValidationInfo(priv); } +SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const +{ + auto tempResult = validateAsync(static_cast(opt), validationTime); + tempResult.first.d_ptr->certificate_status = validateResult(); + return tempResult.first; +} + +class AsyncObjectPrivate +{ /*Currently unused. Created for abi future proofing*/ +}; + +AsyncObject::AsyncObject() : QObject(nullptr) { } + +AsyncObject::~AsyncObject() = default; + +std::pair> FormFieldSignature::validateAsync(ValidateOptions opt, const QDateTime &validationTime) const +{ + auto object = std::make_shared(); + FormWidgetSignature *fws = static_cast(m_formData->fm); + const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1; + SignatureInfo *si = fws->validateSignatureAsync(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch, + [obj = std::weak_ptr(object)]() { + if (auto l = obj.lock()) { + // We need to roundtrip over the eventloop + // to ensure callers have a chance of connecting to AsyncObject::done + QMetaObject::invokeMethod( + l.get(), + [innerObj = std::weak_ptr(l)]() { + if (auto innerLocked = innerObj.lock()) { + emit innerLocked->done(); + } + }, + Qt::QueuedConnection); + } + }); + + return { fromInternal(si, fws), object }; +} + +SignatureValidationInfo::CertificateStatus FormFieldSignature::validateResult() const +{ + return fromInternal(static_cast(m_formData->fm)->validateSignatureResult()); +} + FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const { FormWidgetSignature *fws = static_cast(m_formData->fm); - if (fws->signatureType() != unsigned_signature_field) { + if (fws->signatureType() != CryptoSign::SignatureType::unsigned_signature_field) { return FieldAlreadySigned; } @@ -1111,11 +1217,25 @@ FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &output const auto gSignatureText = std::unique_ptr(QStringToUnicodeGooString(data.signatureText())); const auto gSignatureLeftText = std::unique_ptr(QStringToUnicodeGooString(data.signatureLeftText())); - const bool success = - fws->signDocumentWithAppearance(outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), "SHA256", data.password().toUtf8().constData(), reason.get(), location.get(), ownerPwd, userPwd, *gSignatureText, - *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), convertQColor(data.borderColor()), convertQColor(data.backgroundColor())); - - return success ? SigningSuccess : GenericSigningError; + const auto failure = fws->signDocumentWithAppearance(outputFileName.toStdString(), data.certNickname().toStdString(), data.password().toStdString(), reason.get(), location.get(), ownerPwd, userPwd, *gSignatureText, *gSignatureLeftText, + data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), convertQColor(data.borderColor()), convertQColor(data.backgroundColor())); + if (failure) { + switch (failure.value()) { + case CryptoSign::SigningError::GenericError: + return GenericSigningError; + case CryptoSign::SigningError::InternalError: + return InternalError; + case CryptoSign::SigningError::KeyMissing: + return KeyMissing; + case CryptoSign::SigningError::UserCancelled: + return UserCancelled; + case CryptoSign::SigningError::WriteFailed: + return WriteFailed; + case CryptoSign::SigningError::BadPassphrase: + return BadPassphrase; + } + } + return SigningSuccess; } bool hasNSSSupport() @@ -1129,24 +1249,114 @@ bool hasNSSSupport() QVector getAvailableSigningCertificates() { + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + return {}; + } QVector vReturnCerts; - -#ifdef ENABLE_NSS3 - std::vector> vCerts = SignatureHandler::getAvailableSigningCertificates(); + std::vector> vCerts = backend->getAvailableSigningCertificates(); for (auto &cert : vCerts) { CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get()); vReturnCerts.append(CertificateInfo(certPriv)); } -#endif return vReturnCerts; } +static std::optional convertToFrontend(std::optional type) +{ + if (!type) { + return std::nullopt; + } + switch (type.value()) { + case CryptoSign::Backend::Type::NSS3: + return CryptoSignBackend::NSS; + case CryptoSign::Backend::Type::GPGME: + return CryptoSignBackend::GPG; + } + return std::nullopt; +} + +static std::optional convertToBackend(std::optional backend) +{ + if (!backend) { + return std::nullopt; + } + + switch (backend.value()) { + case CryptoSignBackend::NSS: + return CryptoSign::Backend::Type::NSS3; + case CryptoSignBackend::GPG: + return CryptoSign::Backend::Type::GPGME; + } + return std::nullopt; +} + +QVector availableCryptoSignBackends() +{ + QVector backends; + for (auto &backend : CryptoSign::Factory::getAvailable()) { + auto converted = convertToFrontend(backend); + if (converted) { + backends.push_back(converted.value()); + } + } + return backends; +} + +std::optional activeCryptoSignBackend() +{ + return convertToFrontend(CryptoSign::Factory::getActive()); +} + +bool setActiveCryptoSignBackend(CryptoSignBackend backend) +{ + auto available = availableCryptoSignBackends(); + if (!available.contains(backend)) { + return false; + } + auto converted = convertToBackend(backend); + if (!converted) { + return false; + } + CryptoSign::Factory::setPreferredBackend(converted.value()); + return activeCryptoSignBackend() == backend; +} + +static bool hasNSSBackendFeature(CryptoSignBackendFeature feature) +{ + switch (feature) { + case CryptoSignBackendFeature::BackendAsksPassphrase: + return false; + } + return false; +} + +static bool hasGPGBackendFeature(CryptoSignBackendFeature feature) +{ + switch (feature) { + case CryptoSignBackendFeature::BackendAsksPassphrase: + return true; + } + return false; +} + +bool hasCryptoSignBackendFeature(CryptoSignBackend backend, CryptoSignBackendFeature feature) +{ + switch (backend) { + case CryptoSignBackend::NSS: + return hasNSSBackendFeature(feature); + case CryptoSignBackend::GPG: + return hasGPGBackendFeature(feature); + } + return false; +} + QString POPPLER_QT6_EXPORT getNSSDir() { #ifdef ENABLE_NSS3 - return QString::fromLocal8Bit(SignatureHandler::getNSSDir().c_str()); + return QString::fromLocal8Bit(NSSSignatureConfiguration::getNSSDir().c_str()); #else return QString(); #endif @@ -1159,9 +1369,8 @@ void setNSSDir(const QString &path) return; } - GooString *goo = QStringToGooString(path); - SignatureHandler::setNSSDir(*goo); - delete goo; + const std::unique_ptr goo = QStringToGooString(path); + NSSSignatureConfiguration::setNSSDir(*goo); #else (void)path; #endif @@ -1174,10 +1383,29 @@ std::function nssPasswordCall; void setNSSPasswordCallback(const std::function &f) { #ifdef ENABLE_NSS3 - SignatureHandler::setNSSPasswordCallback(f); + NSSSignatureConfiguration::setNSSPasswordCallback(f); #else qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support"; (void)f; #endif } + +void setPgpSignaturesAllowed(bool allowed) +{ +#ifdef ENABLE_GPGME + GpgSignatureConfiguration::setPgpSignaturesAllowed(allowed); +#else + qWarning() << "Trying to enable pgp signatures, but pgp not enabled in this build"; + (void)allowed; +#endif +} + +bool arePgpSignaturesAllowed() +{ +#ifdef ENABLE_GPGME + return GpgSignatureConfiguration::arePgpSignaturesAllowed(); +#else + return false; +#endif +} } diff --git a/qt6/src/poppler-form.h b/qt6/src/poppler-form.h index beba9fc..2a926be 100644 --- a/qt6/src/poppler-form.h +++ b/qt6/src/poppler-form.h @@ -13,6 +13,8 @@ * Copyright (C) 2020, Thorsten Behrens * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Theofilos Intzoglou + * Copyright (C) 2023-2025, g10 Code GmbH, Author: Sune Stolborg Vuorela + * Copyright (C) 2024, Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -449,6 +452,14 @@ class POPPLER_QT6_EXPORT FormFieldChoice : public FormField */ bool canBeSpellChecked() const; + /** + Sets the text inside the Appearance Stream to the specified + \p text + + \since 24.08 + */ + void setAppearanceChoiceText(const QString &text); + private: Q_DISABLE_COPY(FormFieldChoice) }; @@ -499,6 +510,35 @@ class POPPLER_QT6_EXPORT CertificateInfo Organization, }; + /** + * The type of the certificate + * + * \since 25.03 + */ + enum class CertificateType + { + X509, + PGP + }; + + /** A signing key can be located in different places + sometimes. For the user, it might be easier to pick + the key located on a card if it has some visual + indicator that it is somehow removable. + + \note a keylocation for a certificate without a private + key (cannot be used for signing) will likely be "Unknown" + + \since 23.09 + */ + enum class KeyLocation + { + Unknown, /** We don't know the location */ + Other, /** We know the location, but it is somehow not covered by this enum */ + Computer, /** The key is on this computer */ + HardwareToken /** The key is on a dedicated hardware token, either a smartcard or a dedicated usb token (e.g. gnuk, nitrokey or yubikey) */ + }; + CertificateInfo(); explicit CertificateInfo(CertificateInfoPrivate *priv); ~CertificateInfo(); @@ -570,6 +610,20 @@ class POPPLER_QT6_EXPORT CertificateInfo */ bool isSelfSigned() const; + /** + * Can be used to do qualified electronic signatures (legally binding) + * + * https://en.wikipedia.org/wiki/Qualified_electronic_signature + */ + bool isQualified() const; + + /** + * \return certificate type + * + * \since 25.03 + */ + CertificateType certificateType() const; + /** The DER encoded certificate. */ @@ -582,6 +636,13 @@ class POPPLER_QT6_EXPORT CertificateInfo */ bool checkPassword(const QString &password) const; + /** + The storage location for this key + + \since 23.09 + */ + KeyLocation keyLocation() const; + CertificateInfo(const CertificateInfo &other); CertificateInfo &operator=(const CertificateInfo &other); @@ -624,7 +685,8 @@ class POPPLER_QT6_EXPORT SignatureValidationInfo CertificateRevoked, ///< The certificate was revoked by the issuing certificate authority. CertificateExpired, ///< The signing time is outside the validity bounds of this certificate. CertificateGenericError, ///< The certificate could not be verified. - CertificateNotVerified ///< The certificate is not yet verified. + CertificateNotVerified, ///< The certificate is not yet verified. + CertificateVerificationInProgress ///< The certificate is not yet verified but is in progress in the background. See \ref validateAsync \since 24.05 }; /** @@ -713,10 +775,32 @@ class POPPLER_QT6_EXPORT SignatureValidationInfo private: Q_DECLARE_PRIVATE(SignatureValidationInfo) - + friend class FormFieldSignature; QSharedPointer d_ptr; }; +/** + * Object help waiting for some async event + * + * \since 24.05 + */ +class AsyncObjectPrivate; +class POPPLER_QT6_EXPORT AsyncObject : public QObject // clazy:exclude=ctor-missing-parent-argument +{ + Q_OBJECT +public: + /* Constructor. On purpose not having a QObject parameter + It will be returned by shared_ptr or unique_ptr + */ + AsyncObject(); + ~AsyncObject() override; +Q_SIGNALS: + void done(); + +private: + std::unique_ptr d; +}; + /** A form field that represents a signature. */ @@ -732,7 +816,8 @@ class POPPLER_QT6_EXPORT FormFieldSignature : public FormField AdbePkcs7sha1, AdbePkcs7detached, EtsiCAdESdetached, - UnsignedSignature ///< \since 22.02 + UnsignedSignature, ///< \since 22.02 + G10cPgpSignatureDetached, ///< nonstandardized signature type \since 25.01 }; /** @@ -762,24 +847,76 @@ class POPPLER_QT6_EXPORT FormFieldSignature : public FormField Validate the signature with now as validation time. Reset signature validatation info of scoped instance. + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background + */ + POPPLER_QT6_DEPRECATED SignatureValidationInfo validate(ValidateOptions opt) const; + + /** + Validate the signature with @p validationTime as validation time. + + Reset signature validatation info of scoped instance. + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background */ - SignatureValidationInfo validate(ValidateOptions opt) const; + POPPLER_QT6_DEPRECATED SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; /** Validate the signature with @p validationTime as validation time. Reset signature validatation info of scoped instance. + + \since 24.05 + + \note depending on the backend, some options are only + partially respected. In case of the NSS backend, the two options + requiring network access, AIAFetch and OCSP, + can be toggled individually. In case of the GPG backend, if either + OCSP is used or AIAFetch is used, the other one is also used. + + \note certificate validation will have started when this function return. See \ref validateResult on how to get certifcate validation + \note connections to \ref AsyncObject must happen by the caller + before returning control to the event loop, else signals is not guaranteed to be delivered + */ + std::pair> validateAsync(ValidateOptions opt, const QDateTime &validationTime = {}) const; + + /** + * \return the updated signature validation info from validateAsync + * \note that this function will block if the result is not yet ready. + * Wait for the \ref AsyncObject::done signal to avoid this function blocking on an inconvenient time + * + * \since 24.05 */ - SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const; + SignatureValidationInfo::CertificateStatus validateResult() const; /** + * For consumers, this enum will be extended. Unknown numbers should probably be + * treated as GenericSigningError + * * \since 22.02 */ enum SigningResult { - FieldAlreadySigned, ///< Trying to sign a field that is already signed - GenericSigningError, - SigningSuccess + FieldAlreadySigned, ///< Trying to sign a field that is already signed \since 24.10 + GenericSigningError, ///< Unclassified error \since 24.10 + SigningSuccess, ///< No error \since 24.10 + InternalError, ///< Unexpected error, likely a bug in poppler \since 24.12 + KeyMissing, ///< Key not found (Either the input key is not from the list or the available keys has changed underneath \since 24.12) + WriteFailed, ///< Write failed (permissions, faulty disk, ...) \since 24.12 + UserCancelled, ///< User cancelled the process \since 24.12 + BadPassphrase, ///< Passphrase didn't work \since 25.03 }; /** @@ -794,13 +931,64 @@ class POPPLER_QT6_EXPORT FormFieldSignature : public FormField private: Q_DISABLE_COPY(FormFieldSignature) }; +/** + * Possible compiled in backends for signature handling + * + * \since 23.06 + */ +enum class CryptoSignBackend +{ + NSS, + GPG +}; + +/** + * The available compiled-in backends + * + * \since 23.06 + */ +QVector POPPLER_QT6_EXPORT availableCryptoSignBackends(); + +/** + * Returns current active backend or nullopt if none is active + * + * \note there will always be an active backend if there is available backends + * + * \since 23.06 + */ +std::optional POPPLER_QT6_EXPORT activeCryptoSignBackend(); + +/** + * Sets active backend + * + * \return true on success + * + * \since 23.06 + */ +bool POPPLER_QT6_EXPORT setActiveCryptoSignBackend(CryptoSignBackend backend); + +enum class CryptoSignBackendFeature +{ + /// If the backend itself out of band requests passwords + /// or if the host applicaion somehow must do it + BackendAsksPassphrase +}; + +/** + * Queries if a backend supports or not supports a given feature. + * + * \since 23.06 + */ +bool POPPLER_QT6_EXPORT hasCryptoSignBackendFeature(CryptoSignBackend, CryptoSignBackendFeature); /** Returns is poppler was compiled with NSS support + \deprecated Use availableBackends instead + \since 21.01 */ -bool POPPLER_QT6_EXPORT hasNSSSupport(); +bool POPPLER_QT6_DEPRECATED POPPLER_QT6_EXPORT hasNSSSupport(); /** Return vector of suitable signing certificates @@ -829,6 +1017,23 @@ void POPPLER_QT6_EXPORT setNSSDir(const QString &pathURL); \since 21.01 */ void POPPLER_QT6_EXPORT setNSSPasswordCallback(const std::function &f); + +/** + * Allow pgp signatures in pdf files (standard-extension, experimental) + * Not supported for NSS backend + * + * \param allowed new value for pgp signatures allowed + * + * \since 25.03 + */ +void POPPLER_QT6_EXPORT setPgpSignaturesAllowed(bool allowed); + +/** + * \returns if pgp signatures are allowed. + * + * \since 25.03 + */ +bool POPPLER_QT6_EXPORT arePgpSignaturesAllowed(); } #endif diff --git a/qt6/src/poppler-link-extractor.cc b/qt6/src/poppler-link-extractor.cc index 32bd6b3..48a8e92 100644 --- a/qt6/src/poppler-link-extractor.cc +++ b/qt6/src/poppler-link-extractor.cc @@ -44,7 +44,7 @@ LinkExtractorOutputDev::LinkExtractorOutputDev(PageData *data) : m_data(data) setDefaultCTM(gfxState.getCTM()); } -LinkExtractorOutputDev::~LinkExtractorOutputDev() { } +LinkExtractorOutputDev::~LinkExtractorOutputDev() = default; void LinkExtractorOutputDev::processLink(::AnnotLink *link) { diff --git a/qt6/src/poppler-link-private.h b/qt6/src/poppler-link-private.h index edf334b..5038509 100644 --- a/qt6/src/poppler-link-private.h +++ b/qt6/src/poppler-link-private.h @@ -2,6 +2,7 @@ * Copyright (C) 2016, 2018, 2020, 2021 Albert Astals Cid * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020, 2021 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ #include +#include "poppler-link.h" #include "Link.h" class LinkOCGState; @@ -67,6 +69,27 @@ class LinkHidePrivate : public LinkPrivate bool isShow; }; +class LinkResetFormPrivate : public LinkPrivate +{ +public: + LinkResetFormPrivate(const QRectF &area, const QStringList &fields, const bool exclude) : LinkPrivate(area), m_fields(fields), m_exclude(exclude) { } + ~LinkResetFormPrivate() override; + + QStringList m_fields; + bool m_exclude; +}; + +class LinkSubmitFormPrivate : public LinkPrivate +{ +public: + LinkSubmitFormPrivate(const QRectF &area, const QVector &fieldIds, const QString &url, LinkSubmitForm::SubmitFormFlags flags) : LinkPrivate(area), m_fieldIds(fieldIds), m_url(url), m_flags(flags) { } + ~LinkSubmitFormPrivate() override; + + QVector m_fieldIds; + QString m_url; + LinkSubmitForm::SubmitFormFlags m_flags; +}; + } #endif diff --git a/qt6/src/poppler-link.cc b/qt6/src/poppler-link.cc index deb5a79..8d3c53b 100644 --- a/qt6/src/poppler-link.cc +++ b/qt6/src/poppler-link.cc @@ -1,5 +1,5 @@ /* poppler-link.cc: qt interface to poppler - * Copyright (C) 2006-2007, 2013, 2016-2021, Albert Astals Cid + * Copyright (C) 2006-2007, 2013, 2016-2021, 2024, Albert Astals Cid * Copyright (C) 2007-2008, Pino Toscano * Copyright (C) 2010 Hib Eris * Copyright (C) 2012, Tobias Koenig @@ -7,6 +7,7 @@ * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2018 Adam Reichold * Copyright (C) 2020, 2021 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -75,6 +76,10 @@ LinkOCGStatePrivate::~LinkOCGStatePrivate() = default; LinkHidePrivate::~LinkHidePrivate() = default; +LinkResetFormPrivate::~LinkResetFormPrivate() = default; + +LinkSubmitFormPrivate::~LinkSubmitFormPrivate() = default; + class LinkGotoPrivate : public LinkPrivate { public: @@ -152,17 +157,17 @@ LinkSoundPrivate::~LinkSoundPrivate() class LinkRenditionPrivate : public LinkPrivate { public: - LinkRenditionPrivate(const QRectF &area, ::MediaRendition *rendition, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); + LinkRenditionPrivate(const QRectF &area, std::unique_ptr<::MediaRendition> &&r, ::LinkRendition::RenditionOperation operation, const QString &script, const Ref ref); ~LinkRenditionPrivate() override; - MediaRendition *rendition; + std::unique_ptr rendition; LinkRendition::RenditionAction action; QString script; Ref annotationReference; }; -LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition *r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref) - : LinkPrivate(area), rendition(r ? new MediaRendition(r) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref) +LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, std::unique_ptr<::MediaRendition> &&r, ::LinkRendition::RenditionOperation operation, const QString &javaScript, const Ref ref) + : LinkPrivate(area), rendition(r ? new MediaRendition(std::move(r)) : nullptr), action(LinkRendition::PlayRendition), script(javaScript), annotationReference(ref) { switch (operation) { case ::LinkRendition::NoRendition: @@ -183,10 +188,7 @@ LinkRenditionPrivate::LinkRenditionPrivate(const QRectF &area, ::MediaRendition } } -LinkRenditionPrivate::~LinkRenditionPrivate() -{ - delete rendition; -} +LinkRenditionPrivate::~LinkRenditionPrivate() = default; class LinkJavaScriptPrivate : public LinkPrivate { @@ -315,9 +317,9 @@ LinkDestination::LinkDestination(const QString &description) : d(new LinkDestina } } -LinkDestination::LinkDestination(const LinkDestination &other) : d(other.d) { } +LinkDestination::LinkDestination(const LinkDestination &other) = default; -LinkDestination::~LinkDestination() { } +LinkDestination::~LinkDestination() = default; LinkDestination::Kind LinkDestination::kind() const { @@ -372,15 +374,15 @@ bool LinkDestination::isChangeZoom() const QString LinkDestination::toString() const { QString s = QString::number((qint8)d->kind); - s += ";" + QString::number(d->pageNum); - s += ";" + QString::number(d->left); - s += ";" + QString::number(d->bottom); - s += ";" + QString::number(d->right); - s += ";" + QString::number(d->top); - s += ";" + QString::number(d->zoom); - s += ";" + QString::number((qint8)d->changeLeft); - s += ";" + QString::number((qint8)d->changeTop); - s += ";" + QString::number((qint8)d->changeZoom); + s += ';' + QString::number(d->pageNum); + s += ';' + QString::number(d->left); + s += ';' + QString::number(d->bottom); + s += ';' + QString::number(d->right); + s += ';' + QString::number(d->top); + s += ';' + QString::number(d->zoom); + s += ';' + QString::number((qint8)d->changeLeft); + s += ';' + QString::number((qint8)d->changeTop); + s += ';' + QString::number((qint8)d->changeZoom); return s; } @@ -437,7 +439,7 @@ LinkGoto::LinkGoto(const QRectF &linkArea, const QString &extFileName, const Lin d->extFileName = extFileName; } -LinkGoto::~LinkGoto() { } +LinkGoto::~LinkGoto() = default; bool LinkGoto::isExternal() const { @@ -470,7 +472,7 @@ LinkExecute::LinkExecute(const QRectF &linkArea, const QString &file, const QStr d->parameters = params; } -LinkExecute::~LinkExecute() { } +LinkExecute::~LinkExecute() = default; QString LinkExecute::fileName() const { @@ -495,7 +497,7 @@ LinkBrowse::LinkBrowse(const QRectF &linkArea, const QString &url) : Link(*new L d->url = url; } -LinkBrowse::~LinkBrowse() { } +LinkBrowse::~LinkBrowse() = default; QString LinkBrowse::url() const { @@ -515,7 +517,7 @@ LinkAction::LinkAction(const QRectF &linkArea, ActionType actionType) : Link(*ne d->type = actionType; } -LinkAction::~LinkAction() { } +LinkAction::~LinkAction() = default; LinkAction::ActionType LinkAction::actionType() const { @@ -539,7 +541,7 @@ LinkSound::LinkSound(const QRectF &linkArea, double volume, bool sync, bool repe d->sound = sound; } -LinkSound::~LinkSound() { } +LinkSound::~LinkSound() = default; Link::LinkType LinkSound::linkType() const { @@ -578,11 +580,16 @@ SoundObject *LinkSound::sound() const // LinkRendition LinkRendition::LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref annotationReference) - : Link(*new LinkRenditionPrivate(linkArea, rendition, static_cast(operation), script, annotationReference)) + : LinkRendition(linkArea, std::unique_ptr<::MediaRendition>(rendition), operation, script, annotationReference) +{ +} + +LinkRendition::LinkRendition(const QRectF &linkArea, std::unique_ptr<::MediaRendition> &&rendition, int operation, const QString &script, const Ref annotationReference) + : Link(*new LinkRenditionPrivate(linkArea, std::move(rendition), static_cast(operation), script, annotationReference)) { } -LinkRendition::~LinkRendition() { } +LinkRendition::~LinkRendition() = default; Link::LinkType LinkRendition::linkType() const { @@ -592,7 +599,7 @@ Link::LinkType LinkRendition::linkType() const MediaRendition *LinkRendition::rendition() const { Q_D(const LinkRendition); - return d->rendition; + return d->rendition.get(); } LinkRendition::RenditionAction LinkRendition::action() const @@ -624,7 +631,7 @@ LinkJavaScript::LinkJavaScript(const QRectF &linkArea, const QString &js) : Link d->js = js; } -LinkJavaScript::~LinkJavaScript() { } +LinkJavaScript::~LinkJavaScript() = default; Link::LinkType LinkJavaScript::linkType() const { @@ -640,7 +647,7 @@ QString LinkJavaScript::script() const // LinkMovie LinkMovie::LinkMovie(const QRectF &linkArea, Operation operation, const QString &annotationTitle, const Ref annotationReference) : Link(*new LinkMoviePrivate(linkArea, operation, annotationTitle, annotationReference)) { } -LinkMovie::~LinkMovie() { } +LinkMovie::~LinkMovie() = default; Link::LinkType LinkMovie::linkType() const { @@ -667,7 +674,7 @@ bool LinkMovie::isReferencedAnnotation(const MovieAnnotation *annotation) const LinkOCGState::LinkOCGState(LinkOCGStatePrivate *ocgp) : Link(*ocgp) { } -LinkOCGState::~LinkOCGState() { } +LinkOCGState::~LinkOCGState() = default; Link::LinkType LinkOCGState::linkType() const { @@ -677,7 +684,7 @@ Link::LinkType LinkOCGState::linkType() const // LinkHide LinkHide::LinkHide(LinkHidePrivate *lhidep) : Link(*lhidep) { } -LinkHide::~LinkHide() { } +LinkHide::~LinkHide() = default; Link::LinkType LinkHide::linkType() const { @@ -695,4 +702,58 @@ bool LinkHide::isShowAction() const Q_D(const LinkHide); return d->isShow; } + +// LinkResetForm +LinkResetForm::LinkResetForm(LinkResetFormPrivate *lrfp) : Link(*lrfp) { } + +LinkResetForm::~LinkResetForm() = default; + +Link::LinkType LinkResetForm::linkType() const +{ + return ResetForm; +} + +// LinkSubmitForm +// static assertions to ensure flags from Link.h match those from poppler-link.h +static_assert(static_cast(LinkSubmitForm::NoOpFlag) == static_cast(::LinkSubmitForm::NoOpFlag), "NoOpFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExcludeFlag) == static_cast(::LinkSubmitForm::ExcludeFlag), "ExcludeFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeNoValueFieldsFlag) == static_cast(::LinkSubmitForm::IncludeNoValueFieldsFlag), "IncludeNoValueFieldsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExportFormatFlag) == static_cast(::LinkSubmitForm::ExportFormatFlag), "ExportFormatFlag does not match"); +static_assert(static_cast(LinkSubmitForm::GetMethodFlag) == static_cast(::LinkSubmitForm::GetMethodFlag), "GetMethodFlag does not match"); +static_assert(static_cast(LinkSubmitForm::SubmitCoordinatesFlag) == static_cast(::LinkSubmitForm::SubmitCoordinatesFlag), "SubmitCoordinatesFlag does not match"); +static_assert(static_cast(LinkSubmitForm::XFDFFlag) == static_cast(::LinkSubmitForm::XFDFFlag), "XFDFFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeAppendSavesFlag) == static_cast(::LinkSubmitForm::IncludeAppendSavesFlag), "IncludeAppendSavesFlag does not match"); +static_assert(static_cast(LinkSubmitForm::IncludeAnnotationsFlag) == static_cast(::LinkSubmitForm::IncludeAnnotationsFlag), "IncludeAnnotationsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::SubmitPDFFlag) == static_cast(::LinkSubmitForm::SubmitPDFFlag), "SubmitPDFFlag does not match"); +static_assert(static_cast(LinkSubmitForm::CanonicalFormatFlag) == static_cast(::LinkSubmitForm::CanonicalFormatFlag), "CanonicalFormatFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExclNonUserAnnotsFlag) == static_cast(::LinkSubmitForm::ExclNonUserAnnotsFlag), "ExclNonUserAnnotsFlag does not match"); +static_assert(static_cast(LinkSubmitForm::ExclFKeyFlag) == static_cast(::LinkSubmitForm::ExclFKeyFlag), "ExclFKeyFlag does not match"); +static_assert(static_cast(LinkSubmitForm::EmbedFormFlag) == static_cast(::LinkSubmitForm::EmbedFormFlag), "EmbedFormFlag does not match"); + +LinkSubmitForm::LinkSubmitForm(LinkSubmitFormPrivate *lsfp) : Link(*lsfp) { } + +LinkSubmitForm::~LinkSubmitForm() = default; + +Link::LinkType LinkSubmitForm::linkType() const +{ + return SubmitForm; +} + +QVector LinkSubmitForm::getFieldIds() const +{ + Q_D(const LinkSubmitForm); + return d->m_fieldIds; +} + +QString LinkSubmitForm::getUrl() const +{ + Q_D(const LinkSubmitForm); + return d->m_url; +} + +LinkSubmitForm::SubmitFormFlags LinkSubmitForm::getFlags() const +{ + Q_D(const LinkSubmitForm); + return d->m_flags; +} } diff --git a/qt6/src/poppler-link.h b/qt6/src/poppler-link.h index 52065a2..15ddc3e 100644 --- a/qt6/src/poppler-link.h +++ b/qt6/src/poppler-link.h @@ -1,11 +1,12 @@ /* poppler-link.h: qt interface to poppler - * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, Albert Astals Cid + * Copyright (C) 2006, 2013, 2016, 2018, 2019, 2021, 2022, 2024, Albert Astals Cid * Copyright (C) 2007-2008, 2010, Pino Toscano * Copyright (C) 2010, 2012, Guillermo Amaral * Copyright (C) 2012, Tobias Koenig * Copyright (C) 2013, Anthony Granger * Copyright (C) 2018 Intevation GmbH * Copyright (C) 2020, 2021 Oliver Sander + * Copyright (C) 2024 Pratham Gandhi * Adapting code from * Copyright (C) 2004 by Enrico Ros * @@ -33,6 +34,8 @@ #include #include "poppler-export.h" +#include + struct Ref; class MediaRendition; @@ -51,6 +54,8 @@ class LinkDestinationPrivate; class LinkRenditionPrivate; class LinkOCGStatePrivate; class LinkHidePrivate; +class LinkResetFormPrivate; +class LinkSubmitFormPrivate; class MediaRendition; class MovieAnnotation; class ScreenAnnotation; @@ -199,6 +204,8 @@ class POPPLER_QT6_EXPORT Link JavaScript, ///< A JavaScript code to be interpreted OCGState, ///< An Optional Content Group state change Hide, ///< An action to hide a field + ResetForm, ///< An action to reset the form \since 24.07 + SubmitForm, ///< An action to submit a form \since 24.10 }; /** @@ -488,7 +495,18 @@ class POPPLER_QT6_EXPORT LinkRendition : public Link * \param script the java script code * \param annotationReference the object reference of the screen annotation associated with this rendition action */ - LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref annotationReference); + [[deprecated]] LinkRendition(const QRectF &linkArea, ::MediaRendition *rendition, int operation, const QString &script, const Ref annotationReference); + + /** + * Create a new rendition link. + * + * \param linkArea the active area of the link + * \param rendition the media rendition object. + * \param operation the numeric operation (action) (@see ::LinkRendition::RenditionOperation) + * \param script the java script code + * \param annotationReference the object reference of the screen annotation associated with this rendition action + */ + LinkRendition(const QRectF &linkArea, std::unique_ptr<::MediaRendition> &&rendition, int operation, const QString &script, const Ref annotationReference); /** * Destructor. @@ -655,6 +673,92 @@ class POPPLER_QT6_EXPORT LinkHide : public Link Q_DISABLE_COPY(LinkHide) }; +/** + * ResetForm: an action to reset form fields. + * + * \since 24.07 + */ +class POPPLER_QT6_EXPORT LinkResetForm : public Link +{ + friend class Document; + +public: + /** + * Creates a new ResetForm link. This is only used by Poppler::Page + */ + explicit LinkResetForm(LinkResetFormPrivate *lrfp); + /* + * Destructor + */ + ~LinkResetForm() override; + + LinkType linkType() const override; + +private: + Q_DECLARE_PRIVATE(LinkResetForm) + Q_DISABLE_COPY(LinkResetForm) +}; + +/** + * SubmitForm : An action to submit a form. + * + * \since 24.10 + */ + +class POPPLER_QT6_EXPORT LinkSubmitForm : public Link +{ +public: + enum SubmitFormFlag + { + NoOpFlag = 0, + ExcludeFlag = 1, + IncludeNoValueFieldsFlag = 1 << 1, + ExportFormatFlag = 1 << 2, + GetMethodFlag = 1 << 3, + SubmitCoordinatesFlag = 1 << 4, + XFDFFlag = 1 << 5, + IncludeAppendSavesFlag = 1 << 6, + IncludeAnnotationsFlag = 1 << 7, + SubmitPDFFlag = 1 << 8, + CanonicalFormatFlag = 1 << 9, + ExclNonUserAnnotsFlag = 1 << 10, + ExclFKeyFlag = 1 << 11, + // 13th high bit flag is undefined + EmbedFormFlag = 1 << 13, + }; + Q_DECLARE_FLAGS(SubmitFormFlags, SubmitFormFlag) + + /** + * Create a new SubmitForm link. + */ + explicit LinkSubmitForm(LinkSubmitFormPrivate *lsfp); + /** + * Destructor + */ + ~LinkSubmitForm() override; + + LinkType linkType() const override; + + /** + * The ids of fields that are to be submitted. + */ + QVector getFieldIds() const; + + /** + * The url to which the form is to be submitted. + */ + QString getUrl() const; + + /** + * The flags specifying how the form should be submitted. + */ + SubmitFormFlags getFlags() const; + +private: + Q_DECLARE_PRIVATE(LinkSubmitForm) + Q_DISABLE_COPY(LinkSubmitForm) +}; + } #endif diff --git a/qt6/src/poppler-media.cc b/qt6/src/poppler-media.cc index cf634ca..6f0ccdb 100644 --- a/qt6/src/poppler-media.cc +++ b/qt6/src/poppler-media.cc @@ -1,6 +1,7 @@ /* poppler-media.cc: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. - * Copyright (C) 2013, 2018, 2021 Albert Astals Cid + * Copyright (C) 2013, 2018, 2021, 2024 Albert Astals Cid + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,17 +33,19 @@ namespace Poppler { class MediaRenditionPrivate { public: - explicit MediaRenditionPrivate(::MediaRendition *renditionA) : rendition(renditionA) { } + explicit MediaRenditionPrivate(std::unique_ptr<::MediaRendition> &&renditionA) : rendition(std::move(renditionA)) { } - ~MediaRenditionPrivate() { delete rendition; } + ~MediaRenditionPrivate() = default; MediaRenditionPrivate(const MediaRenditionPrivate &) = delete; MediaRenditionPrivate &operator=(const MediaRenditionPrivate &) = delete; - ::MediaRendition *rendition; + std::unique_ptr<::MediaRendition> rendition; }; -MediaRendition::MediaRendition(::MediaRendition *rendition) : d_ptr(new MediaRenditionPrivate(rendition)) { } +MediaRendition::MediaRendition(::MediaRendition *rendition) : MediaRendition(std::unique_ptr<::MediaRendition>(rendition)) { } + +MediaRendition::MediaRendition(std::unique_ptr<::MediaRendition> &&rendition) : d_ptr(new MediaRenditionPrivate(std::move(rendition))) { } MediaRendition::~MediaRendition() { @@ -86,12 +89,15 @@ QByteArray MediaRendition::data() const return QByteArray(); } + if (!s->reset()) { + return QByteArray {}; + } + QBuffer buffer; unsigned char data[BUFFER_MAX]; int bread; buffer.open(QIODevice::WriteOnly); - s->reset(); while ((bread = s->doGetChars(BUFFER_MAX, data)) != 0) { buffer.write(reinterpret_cast(data), bread); } diff --git a/qt6/src/poppler-media.h b/qt6/src/poppler-media.h index 83bd329..f794b55 100644 --- a/qt6/src/poppler-media.h +++ b/qt6/src/poppler-media.h @@ -1,6 +1,6 @@ /* poppler-media.h: qt interface to poppler * Copyright (C) 2012 Guillermo A. Amaral B. - * Copyright (C) 2012, 2013, 2021 Albert Astals Cid + * Copyright (C) 2012, 2013, 2021, 2024 Albert Astals Cid * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,7 +40,13 @@ class POPPLER_QT6_EXPORT MediaRendition /** Constructs a MediaRendition. Takes ownership of the passed rendition */ - explicit MediaRendition(::MediaRendition *rendition); + [[deprecated]] explicit MediaRendition(::MediaRendition *rendition); + + /** + Constructs a MediaRendition. + */ + explicit MediaRendition(std::unique_ptr<::MediaRendition> &&rendition); + ~MediaRendition(); /** diff --git a/qt6/src/poppler-optcontent-private.h b/qt6/src/poppler-optcontent-private.h index 589fb0b..0b1046d 100644 --- a/qt6/src/poppler-optcontent-private.h +++ b/qt6/src/poppler-optcontent-private.h @@ -4,6 +4,7 @@ * Copyright (C) 2008, Pino Toscano * Copyright (C) 2016, 2018, 2019, 2021, Albert Astals Cid * Copyright (C) 2017, Hubert Figuière + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -97,13 +98,13 @@ class OptContentItem class OptContentModelPrivate { public: - OptContentModelPrivate(OptContentModel *qq, OCGs *optContent); + OptContentModelPrivate(OptContentModel *qq, const OCGs *optContent); ~OptContentModelPrivate(); OptContentModelPrivate(const OptContentModelPrivate &) = delete; OptContentModelPrivate &operator=(const OptContentModelPrivate &) = delete; - void parseRBGroupsArray(Array *rBGroupArray); + void parseRBGroupsArray(const Array *rBGroupArray); OptContentItem *nodeFromIndex(const QModelIndex &index, bool canBeNull = false) const; QModelIndex indexFromItem(OptContentItem *node, int column) const; @@ -126,7 +127,7 @@ class OptContentModelPrivate private: void addChild(OptContentItem *parent, OptContentItem *child); - void parseOrderArray(OptContentItem *parentNode, Array *orderArray); + void parseOrderArray(OptContentItem *parentNode, const Array *orderArray); }; } diff --git a/qt6/src/poppler-optcontent.cc b/qt6/src/poppler-optcontent.cc index 7489966..08acc09 100644 --- a/qt6/src/poppler-optcontent.cc +++ b/qt6/src/poppler-optcontent.cc @@ -8,6 +8,7 @@ * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019, 2020 Oliver Sander + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,7 +55,7 @@ RadioButtonGroup::RadioButtonGroup(OptContentModelPrivate *ocModel, Array *rbarr } } -RadioButtonGroup::~RadioButtonGroup() { } +RadioButtonGroup::~RadioButtonGroup() = default; QSet RadioButtonGroup::setItemOn(OptContentItem *itemToSetOn) { @@ -95,7 +96,7 @@ OptContentItem::OptContentItem(const QString &label) OptContentItem::OptContentItem() : m_parent(nullptr), m_enabled(true) { } -OptContentItem::~OptContentItem() { } +OptContentItem::~OptContentItem() = default; void OptContentItem::appendRBGroup(RadioButtonGroup *rbgroup) { @@ -151,7 +152,7 @@ QSet OptContentItem::recurseListChildren(bool includeMe) const return ret; } -OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, OCGs *optContent) : q(qq) +OptContentModelPrivate::OptContentModelPrivate(OptContentModel *qq, const OCGs *optContent) : q(qq) { m_rootNode = new OptContentItem(); const auto &ocgs = optContent->getOCGs(); @@ -183,7 +184,7 @@ OptContentModelPrivate::~OptContentModelPrivate() delete m_rootNode; } -void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array *orderArray) +void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, const Array *orderArray) { OptContentItem *lastItem = parentNode; for (int i = 0; i < orderArray->getLength(); ++i) { @@ -214,7 +215,7 @@ void OptContentModelPrivate::parseOrderArray(OptContentItem *parentNode, Array * } } -void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) +void OptContentModelPrivate::parseRBGroupsArray(const Array *rBGroupArray) { if (!rBGroupArray) { return; @@ -232,7 +233,7 @@ void OptContentModelPrivate::parseRBGroupsArray(Array *rBGroupArray) } } -OptContentModel::OptContentModel(OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) +OptContentModel::OptContentModel(const OCGs *optContent, QObject *parent) : QAbstractItemModel(parent) { d = new OptContentModelPrivate(this, optContent); } @@ -349,7 +350,7 @@ bool OptContentModel::setData(const QModelIndex &index, const QVariant &value, i Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } - std::stable_sort(indexes.begin(), indexes.end()); + std::stable_sort(indexes.begin(), indexes.end()); // NOLINT(modernize-use-ranges) We need Qt 6.8 for QModelIndexList to support ranges and our minimum requirement is 6.2 Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } @@ -410,7 +411,7 @@ void OptContentModel::applyLink(LinkOCGState *link) Q_FOREACH (OptContentItem *item, changedItems) { indexes.append(d->indexFromItem(item, 0)); } - std::stable_sort(indexes.begin(), indexes.end()); + std::stable_sort(indexes.begin(), indexes.end()); // NOLINT(modernize-use-ranges) We need Qt 6.8 for QModelIndexList to support ranges and our minimum requirement is 6.2 Q_FOREACH (const QModelIndex &changedIndex, indexes) { emit dataChanged(changedIndex, changedIndex); } diff --git a/qt6/src/poppler-optcontent.h b/qt6/src/poppler-optcontent.h index 6b9467a..8e4334f 100644 --- a/qt6/src/poppler-optcontent.h +++ b/qt6/src/poppler-optcontent.h @@ -4,6 +4,7 @@ * Copyright (C) 2008, Pino Toscano * Copyright (C) 2013, Anthony Granger * Copyright (C) 2016, 2021, Albert Astals Cid + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,7 +72,7 @@ class POPPLER_QT6_EXPORT OptContentModel : public QAbstractItemModel void applyLink(LinkOCGState *link); private: - explicit OptContentModel(OCGs *optContent, QObject *parent = nullptr); + explicit OptContentModel(const OCGs *optContent, QObject *parent = nullptr); friend class OptContentModelPrivate; OptContentModelPrivate *d; diff --git a/qt6/src/poppler-outline.cc b/qt6/src/poppler-outline.cc index f5ba2a9..7b0c9f1 100644 --- a/qt6/src/poppler-outline.cc +++ b/qt6/src/poppler-outline.cc @@ -3,6 +3,7 @@ * Copyright (C) 2018 Adam Reichold * Copyright (C) 2019 Oliver Sander * Copyright (C) 2019 Albert Astals Cid + * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,7 +79,7 @@ QString OutlineItem::name() const if (name.isEmpty()) { if (const ::OutlineItem *data = m_data->data) { - name = unicodeToQString(data->getTitle(), data->getTitleLength()); + name = unicodeToQString(data->getTitle()); } } diff --git a/qt6/src/poppler-page.cc b/qt6/src/poppler-page.cc index dd1cb0e..0a47209 100644 --- a/qt6/src/poppler-page.cc +++ b/qt6/src/poppler-page.cc @@ -1,7 +1,7 @@ /* poppler-page.cc: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, Brad Hards - * Copyright (C) 2005-2022, Albert Astals Cid + * Copyright (C) 2005-2022, 2024, Albert Astals Cid * Copyright (C) 2005, Stefan Kebekus * Copyright (C) 2006-2011, Pino Toscano * Copyright (C) 2008 Carlos Garcia Campos @@ -26,6 +26,10 @@ * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Thomas Huxhorn + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines + * Copyright (C) 2024 Stefan Brüns + * Copyright (C) 2024 Pratham Gandhi + * Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -128,10 +132,6 @@ class Qt6SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelpe { SplashBitmap *b = getBitmap(); - const int bw = b->getWidth(); - const int bh = b->getHeight(); - const int brs = b->getRowSize(); - // If we use DeviceN8, convert to XBGR8. // If requested, also transfer Splash's internal alpha channel. const SplashBitmap::ConversionMode mode = ignorePaperColor ? SplashBitmap::conversionAlphaPremultiplied : SplashBitmap::conversionOpaque; @@ -139,6 +139,10 @@ class Qt6SplashOutputDev : public SplashOutputDev, public OutputDevCallbackHelpe const QImage::Format format = ignorePaperColor ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; if (b->convertToXBGR(mode)) { + const int bw = b->getWidth(); + const int bh = b->getHeight(); + const int brs = b->getRowSize(); + SplashColorPtr data = takeImageData ? b->takeData() : b->getDataPtr(); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { @@ -225,7 +229,7 @@ std::unique_ptr PageData::convertLinkActionToLink(::LinkAction *a, Documen case actionLaunch: { LinkLaunch *e = (LinkLaunch *)a; const GooString *p = e->getParams(); - popplerLink = std::make_unique(linkArea, e->getFileName()->c_str(), p ? p->c_str() : nullptr); + popplerLink = std::make_unique(linkArea, UnicodeParsedString(e->getFileName()), p ? p->c_str() : nullptr); } break; case actionNamed: { @@ -315,7 +319,7 @@ std::unique_ptr PageData::convertLinkActionToLink(::LinkAction *a, Documen reference = lrn->getScreenAnnot(); } - popplerLink = std::make_unique(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : nullptr, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); + popplerLink = std::make_unique(linkArea, lrn->getMedia() ? lrn->getMedia()->copy() : std::unique_ptr<::MediaRendition> {}, lrn->getOperation(), UnicodeParsedString(lrn->getScript()), reference); } break; case actionOCGState: { @@ -332,9 +336,40 @@ std::unique_ptr PageData::convertLinkActionToLink(::LinkAction *a, Documen popplerLink = std::make_unique(lhp); } break; - case actionResetForm: - // Not handled in Qt6 front-end yet - break; + case actionResetForm: { + ::LinkResetForm *lrf = (::LinkResetForm *)a; + std::vector stdStringFields = lrf->getFields(); + QStringList qStringFields; + for (const std::string &str : stdStringFields) { + qStringFields << QString::fromStdString(str); + } + LinkResetFormPrivate *lrfp = new LinkResetFormPrivate(linkArea, qStringFields, lrf->getExclude()); + popplerLink = std::make_unique(lrfp); + } break; + + case actionSubmitForm: { + ::LinkSubmitForm *lsf = (::LinkSubmitForm *)a; + const std::vector &stdStringFields = lsf->getFields(); + QVector fieldIds; + fieldIds.reserve(stdStringFields.size()); + Form *form = parentDoc->doc->getCatalog()->getForm(); + for (const std::string &fieldStr : stdStringFields) { + ::FormField *field = form->findFieldByFullyQualifiedNameOrRef(fieldStr); + if (field && !field->getNoExport()) { + int numWidgets = field->getNumWidgets(); + for (int i = 0; i < numWidgets; i++) { + ::FormWidget *widget = field->getWidget(i); + if (widget) { + fieldIds.append(field->getWidget(i)->getID()); + } + } + } + } + QString qStringUrl = QString::fromStdString(lsf->getUrl()); + LinkSubmitForm::SubmitFormFlags qFlags = static_cast(lsf->getFlags()); + LinkSubmitFormPrivate *lsfp = new LinkSubmitFormPrivate(linkArea, fieldIds, qStringUrl, qFlags); + popplerLink = std::make_unique(lsfp); + } break; case actionUnknown: break; @@ -644,7 +679,7 @@ QImage Page::thumbnail() const QString Page::text(const QRectF &r, TextLayout textLayout) const { TextOutputDev *output_dev; - GooString *s; + GooString s; QString result; const bool rawOrder = textLayout == RawOrderLayout; @@ -652,15 +687,18 @@ QString Page::text(const QRectF &r, TextLayout textLayout) const m_page->parentDoc->doc->displayPageSlice(output_dev, m_page->index + 1, 72, 72, 0, false, true, false, -1, -1, -1, -1, nullptr, nullptr, nullptr, nullptr, true); if (r.isNull()) { const PDFRectangle *rect = m_page->page->getCropBox(); - s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2); + if (orientation() == Orientation::Portrait || orientation() == Orientation::UpsideDown) { + s = output_dev->getText(rect->x1, rect->y1, rect->x2, rect->y2); + } else { + s = output_dev->getText(rect->y1, rect->x1, rect->y2, rect->x2); + } } else { s = output_dev->getText(r.left(), r.top(), r.right(), r.bottom()); } - result = QString::fromUtf8(s->c_str()); + result = QString::fromStdString(s.toStr()); delete output_dev; - delete s; return result; } diff --git a/qt6/src/poppler-pdf-converter.cc b/qt6/src/poppler-pdf-converter.cc index 4d3cad1..935d59a 100644 --- a/qt6/src/poppler-pdf-converter.cc +++ b/qt6/src/poppler-pdf-converter.cc @@ -1,6 +1,6 @@ /* poppler-pdf-converter.cc: qt interface to poppler * Copyright (C) 2008, Pino Toscano - * Copyright (C) 2008, 2009, 2020-2022, Albert Astals Cid + * Copyright (C) 2008, 2009, 2020-2022, 2024, Albert Astals Cid * Copyright (C) 2020, Thorsten Behrens * Copyright (C) 2020, Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021, Klarälvdalens Datakonsult AB, a KDAB Group company, . @@ -8,6 +8,7 @@ * Copyright (C) 2021, Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022, Martin * Copyright (C) 2022, Felix Jung + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,7 +51,7 @@ class PDFConverterPrivate : public BaseConverterPrivate PDFConverter::PDFOptions opts; }; -PDFConverterPrivate::PDFConverterPrivate() : BaseConverterPrivate() { } +PDFConverterPrivate::PDFConverterPrivate() = default; PDFConverterPrivate::~PDFConverterPrivate() = default; @@ -60,7 +61,7 @@ PDFConverter::PDFConverter(DocumentData *document) : BaseConverter(*new PDFConve d->document = document; } -PDFConverter::~PDFConverter() { } +PDFConverter::~PDFConverter() = default; void PDFConverter::setPDFOptions(PDFConverter::PDFOptions options) { @@ -140,9 +141,10 @@ bool PDFConverter::sign(const NewSignatureData &data) const auto location = std::unique_ptr(data.location().isEmpty() ? nullptr : QStringToUnicodeGooString(data.location())); const auto ownerPwd = std::optional(data.documentOwnerPassword().constData()); const auto userPwd = std::optional(data.documentUserPassword().constData()); - return doc->sign(d->outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), data.password().toUtf8().constData(), QStringToGooString(data.fieldPartialName()), data.page() + 1, - boundaryToPdfRectangle(destPage, data.boundingRectangle(), Annotation::FixedRotation), *gSignatureText, *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), - convertQColor(data.borderColor()), convertQColor(data.backgroundColor()), reason.get(), location.get(), data.imagePath().toStdString(), ownerPwd, userPwd); + return !doc->sign(d->outputFileName.toUtf8().constData(), data.certNickname().toUtf8().constData(), data.password().toUtf8().constData(), QStringToGooString(data.fieldPartialName()), data.page() + 1, + boundaryToPdfRectangle(destPage, data.boundingRectangle(), Annotation::FixedRotation), *gSignatureText, *gSignatureLeftText, data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), + convertQColor(data.borderColor()), convertQColor(data.backgroundColor()), reason.get(), location.get(), data.imagePath().toStdString(), ownerPwd, userPwd) + .has_value(); } struct PDFConverter::NewSignatureData::NewSignatureDataPrivate diff --git a/qt6/src/poppler-private.cc b/qt6/src/poppler-private.cc index 91d1725..4ed3a3f 100644 --- a/qt6/src/poppler-private.cc +++ b/qt6/src/poppler-private.cc @@ -7,10 +7,12 @@ * Copyright (C) 2016 Jakub Alba * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich * Copyright (C) 2018-2020 Adam Reichold - * Copyright (C) 2019, 2020 Oliver Sander + * Copyright (C) 2019, 2020, 2024 Oliver Sander * Copyright (C) 2019 João Netto * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, * Copyright (C) 2021 Mahmoud Khalil + * Copyright (C) 2023 Shivodit Gill + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -41,6 +43,16 @@ #include #include #include +#include + +#ifdef ANDROID +# include +# include +# include +# include +# include +# include +#endif namespace Poppler { @@ -94,6 +106,11 @@ QString unicodeToQString(const Unicode *u, int len) return QString::fromUtf8(convertedStr.c_str(), convertedStr.getLength()); } +QString unicodeToQString(const std::vector &u) +{ + return unicodeToQString(u.data(), u.size()); +} + QString UnicodeParsedString(const GooString *s1) { return (s1) ? UnicodeParsedString(s1->toStr()) : QString(); @@ -105,48 +122,45 @@ QString UnicodeParsedString(const std::string &s1) return QString(); } - if (GooString::hasUnicodeMarker(s1) || GooString::hasUnicodeMarkerLE(s1)) { + if (hasUnicodeByteOrderMark(s1) || hasUnicodeByteOrderMarkLE(s1)) { return QString::fromUtf16(reinterpret_cast(s1.c_str()), s1.size() / 2); } else { - int stringLength; - const char *cString = pdfDocEncodingToUTF16(s1, &stringLength); - auto result = QString::fromUtf16(reinterpret_cast(cString), stringLength / 2); - delete[] cString; + std::string cString = pdfDocEncodingToUTF16(s1); + auto result = QString::fromUtf16(reinterpret_cast(cString.c_str()), cString.size() / 2); return result; } } -GooString *QStringToUnicodeGooString(const QString &s) +std::unique_ptr QStringToUnicodeGooString(const QString &s) { if (s.isEmpty()) { - return new GooString(); + return std::make_unique(); } int len = s.length() * 2 + 2; - char *cstring = (char *)gmallocn(len, sizeof(char)); - cstring[0] = (char)0xfe; - cstring[1] = (char)0xff; - for (int i = 0; i < s.length(); ++i) { - cstring[2 + i * 2] = s.at(i).row(); - cstring[3 + i * 2] = s.at(i).cell(); + std::string string; + string.reserve(len); + string.push_back((char)0xfe); + string.push_back((char)0xff); + for (auto element : s) { + string.push_back(element.row()); + string.push_back(element.cell()); } - GooString *ret = new GooString(cstring, len); - gfree(cstring); - return ret; + return std::make_unique(string); } -GooString *QStringToGooString(const QString &s) +std::unique_ptr QStringToGooString(const QString &s) { int len = s.length(); char *cstring = (char *)gmallocn(s.length(), sizeof(char)); for (int i = 0; i < len; ++i) { cstring[i] = s.at(i).unicode(); } - GooString *ret = new GooString(cstring, len); + std::unique_ptr ret = std::make_unique(cstring, len); gfree(cstring); return ret; } -GooString *QDateTimeToUnicodeGooString(const QDateTime &dt) +std::unique_ptr QDateTimeToUnicodeGooString(const QDateTime &dt) { if (!dt.isValid()) { return nullptr; @@ -198,6 +212,29 @@ void DocumentData::init() m_optContentModel = nullptr; xrefReconstructed = false; xrefReconstructedCallback = {}; + +#ifdef ANDROID + // Copy fonts from android apk to the app's storage dir, and + // set the font directory path + QString assetsFontDir = QStringLiteral("assets:/share/fonts"); + QString fontsdir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/fonts"); + QDir fontPath = QDir(fontsdir); + + if (fontPath.mkpath(fontPath.absolutePath())) { + GlobalParams::setFontDir(fontPath.absolutePath().toStdString()); + QDirIterator iterator(assetsFontDir, QDir::NoFilter, QDirIterator::Subdirectories); + + while (iterator.hasNext()) { + iterator.next(); + QFileInfo fontFileInfo = iterator.fileInfo(); + QString fontFilePath = assetsFontDir + QStringLiteral("/") + fontFileInfo.fileName(); + QString destPath = fontPath.absolutePath() + QStringLiteral("/") + fontFileInfo.fileName(); + QFile::copy(fontFilePath, destPath); + } + } else { + GlobalParams::setFontDir(""); + } +#endif } void DocumentData::noitfyXRefReconstructed() diff --git a/qt6/src/poppler-private.h b/qt6/src/poppler-private.h index 45ed4b4..219aac8 100644 --- a/qt6/src/poppler-private.h +++ b/qt6/src/poppler-private.h @@ -1,7 +1,7 @@ /* poppler-private.h: qt interface to poppler * Copyright (C) 2005, Net Integration Technologies, Inc. * Copyright (C) 2005, 2008, Brad Hards - * Copyright (C) 2006-2009, 2011, 2012, 2017-2022 by Albert Astals Cid + * Copyright (C) 2006-2009, 2011, 2012, 2017-2022, 2024, 2025 by Albert Astals Cid * Copyright (C) 2007-2009, 2011, 2014 by Pino Toscano * Copyright (C) 2011 Andreas Hartmetz * Copyright (C) 2011 Hib Eris @@ -21,6 +21,7 @@ * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Hubert Figuiere * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. + * Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * Inspired on code by * Copyright (C) 2004 by Albert Astals Cid * Copyright (C) 2004 by Enrico Ros @@ -72,18 +73,19 @@ namespace Poppler { /* borrowed from kpdf */ POPPLER_QT6_EXPORT QString unicodeToQString(const Unicode *u, int len); +POPPLER_QT6_EXPORT QString unicodeToQString(const std::vector &u); POPPLER_QT6_EXPORT QString UnicodeParsedString(const GooString *s1); POPPLER_QT6_EXPORT QString UnicodeParsedString(const std::string &s1); -POPPLER_QT6_EXPORT GooString *QStringToUnicodeGooString(const QString &s); +POPPLER_QT6_EXPORT std::unique_ptr QStringToUnicodeGooString(const QString &s); // Returns a big endian UTF-16 string with BOM or an empty string without BOM. // The caller owns the returned pointer. -POPPLER_QT6_EXPORT GooString *QStringToGooString(const QString &s); +POPPLER_QT6_EXPORT std::unique_ptr QStringToGooString(const QString &s); -GooString *QDateTimeToUnicodeGooString(const QDateTime &dt); +std::unique_ptr QDateTimeToUnicodeGooString(const QDateTime &dt); void qt6ErrorFunction(ErrorCategory /*category*/, Goffset pos, const char *msg); @@ -110,9 +112,9 @@ class DocumentData : private GlobalParamsIniter m_filePath = filePath; #ifdef _WIN32 - doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc((wchar_t *)filePath.utf16(), filePath.length(), ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #else - doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(std::make_unique(QFile::encodeName(filePath).constData()), ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); #endif } @@ -121,7 +123,7 @@ class DocumentData : private GlobalParamsIniter m_device = device; QIODeviceInStream *str = new QIODeviceInStream(device, 0, false, device->size(), Object(objNull)); init(); - doc = new PDFDoc(str, ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(str, ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); } DocumentData(const QByteArray &data, const std::optional &ownerPassword, const std::optional &userPassword) : GlobalParamsIniter(qt6ErrorFunction) @@ -130,7 +132,7 @@ class DocumentData : private GlobalParamsIniter fileContents = data; MemStream *str = new MemStream((char *)fileContents.data(), 0, fileContents.length(), Object(objNull)); init(); - doc = new PDFDoc(str, ownerPassword, userPassword, nullptr, std::bind(&DocumentData::noitfyXRefReconstructed, this)); + doc = new PDFDoc(str, ownerPassword, userPassword, std::bind(&DocumentData::noitfyXRefReconstructed, this)); } void init(); @@ -140,10 +142,7 @@ class DocumentData : private GlobalParamsIniter DocumentData(const DocumentData &) = delete; DocumentData &operator=(const DocumentData &) = delete; - void setPaperColor(const QColor &color) - { - paperColor = color; - } + void setPaperColor(const QColor &color) { paperColor = color; } void fillMembers() { @@ -197,14 +196,17 @@ class FontInfoData explicit FontInfoData(::FontInfo *fi) { - if (fi->getName()) { - fontName = fi->getName()->c_str(); + const std::optional &fiName = fi->getName(); + if (fiName) { + fontName = fiName->c_str(); } - if (fi->getFile()) { - fontFile = fi->getFile()->c_str(); + const std::optional &fiFile = fi->getFile(); + if (fiFile) { + fontFile = fiFile->c_str(); } - if (fi->getSubstituteName()) { - fontSubstituteName = fi->getSubstituteName()->c_str(); + const std::optional &fiSubstituteName = fi->getSubstituteName(); + if (fiSubstituteName) { + fontSubstituteName = fiSubstituteName->c_str(); } isEmbedded = fi->getEmbedded(); isSubset = fi->getSubset(); @@ -229,7 +231,7 @@ class FontIteratorData public: FontIteratorData(int startPage, DocumentData *dd) : fontInfoScanner(dd->doc, startPage), totalPages(dd->doc->getNumPages()), currentPage(qMax(startPage, 0) - 1) { } - ~FontIteratorData() { } + ~FontIteratorData() = default; FontInfoScanner fontInfoScanner; int totalPages; diff --git a/qt6/src/poppler-ps-converter.cc b/qt6/src/poppler-ps-converter.cc index e495c45..8c95536 100644 --- a/qt6/src/poppler-ps-converter.cc +++ b/qt6/src/poppler-ps-converter.cc @@ -7,6 +7,7 @@ * Copyright (C) 2013 Thomas Freitag * Copyright (C) 2014 Adrian Johnson * Copyright (C) 2020 William Bader + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,19 +61,7 @@ class PSConverterPrivate : public BaseConverterPrivate }; PSConverterPrivate::PSConverterPrivate() - : BaseConverterPrivate(), - hDPI(72), - vDPI(72), - rotate(0), - paperWidth(-1), - paperHeight(-1), - marginRight(0), - marginBottom(0), - marginLeft(0), - marginTop(0), - opts(PSConverter::Printing), - pageConvertedCallback(nullptr), - pageConvertedPayload(nullptr) + : hDPI(72), vDPI(72), rotate(0), paperWidth(-1), paperHeight(-1), marginRight(0), marginBottom(0), marginLeft(0), marginTop(0), opts(PSConverter::Printing), pageConvertedCallback(nullptr), pageConvertedPayload(nullptr) { } @@ -84,7 +73,7 @@ PSConverter::PSConverter(DocumentData *document) : BaseConverter(*new PSConverte d->document = document; } -PSConverter::~PSConverter() { } +PSConverter::~PSConverter() = default; void PSConverter::setPageList(const QList &pageList) { @@ -162,6 +151,16 @@ void PSConverter::setStrictMargins(bool strictMargins) } } +void PSConverter::setForceOverprintPreview(bool forceOverprintPreview) +{ + Q_D(PSConverter); + if (forceOverprintPreview) { + d->opts |= ForceOverprintPreview; + } else { + d->opts &= ~ForceOverprintPreview; + } +} + void PSConverter::setForceRasterize(bool forceRasterize) { Q_D(PSConverter); @@ -235,6 +234,10 @@ bool PSConverter::convert() PSOutputDev *psOut = new PSOutputDev(outputToQIODevice, dev, pstitlechar, d->document->doc, pages, (d->opts & PrintToEPS) ? psModeEPS : psModePS, d->paperWidth, d->paperHeight, false, false, d->marginLeft, d->marginBottom, d->paperWidth - d->marginRight, d->paperHeight - d->marginTop, (d->opts & ForceRasterization) ? psAlwaysRasterize : psRasterizeWhenNeeded); + if (d->opts & ForceOverprintPreview) { + psOut->setForceRasterize(psAlwaysRasterize); + psOut->setOverprintPreview(true); + } if (d->opts & StrictMargins) { double xScale = ((double)d->paperWidth - (double)d->marginLeft - (double)d->marginRight) / (double)d->paperWidth; diff --git a/qt6/src/poppler-qiodeviceoutstream-private.h b/qt6/src/poppler-qiodeviceoutstream-private.h index 77ca7d3..63e183c 100644 --- a/qt6/src/poppler-qiodeviceoutstream-private.h +++ b/qt6/src/poppler-qiodeviceoutstream-private.h @@ -3,6 +3,7 @@ * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,6 +39,7 @@ class QIODeviceOutStream : public OutStream void close() override; Goffset getPos() override; void put(char c) override; + size_t write(std::span data) override; void printf(const char *format, ...) override GCC_PRINTF_FORMAT(2, 3); private: diff --git a/qt6/src/poppler-qiodeviceoutstream.cc b/qt6/src/poppler-qiodeviceoutstream.cc index 0a96716..3d3171d 100644 --- a/qt6/src/poppler-qiodeviceoutstream.cc +++ b/qt6/src/poppler-qiodeviceoutstream.cc @@ -3,6 +3,7 @@ * Copyright (C) 2013 Adrian Johnson * Copyright (C) 2020, 2021 Albert Astals Cid * Copyright (C) 2021, Even Rouault + * Copyright (C) 2024, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +30,7 @@ namespace Poppler { QIODeviceOutStream::QIODeviceOutStream(QIODevice *device) : m_device(device) { } -QIODeviceOutStream::~QIODeviceOutStream() { } +QIODeviceOutStream::~QIODeviceOutStream() = default; void QIODeviceOutStream::close() { } @@ -43,6 +44,11 @@ void QIODeviceOutStream::put(char c) m_device->putChar(c); } +size_t QIODeviceOutStream::write(std::span data) +{ + return m_device->write(reinterpret_cast(data.data()), data.size()); +} + static int poppler_vasprintf(char **buf_ptr, const char *format, va_list ap) GCC_PRINTF_FORMAT(2, 0); static int poppler_vasprintf(char **buf_ptr, const char *format, va_list ap) diff --git a/qt6/src/poppler-qt6.h b/qt6/src/poppler-qt6.h index da27d86..2350e3f 100644 --- a/qt6/src/poppler-qt6.h +++ b/qt6/src/poppler-qt6.h @@ -24,11 +24,13 @@ * Copyright (C) 2020 Philipp Knechtges * Copyright (C) 2020 Katarina Behrens * Copyright (C) 2020 Thorsten Behrens - * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden + * Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by Technische Universität Dresden * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, . * Copyright (C) 2021 Mahmoud Khalil * Copyright (C) 2021 Georgiy Sgibnev . Work sponsored by lab50.net. * Copyright (C) 2022 Martin + * Copyright (C) 2023 Kevin Ottens . Work sponsored by De Bortoli Wines + * Copyright (C) 2024 Pratham Gandhi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,6 +58,7 @@ #include "poppler-link.h" #include "poppler-optcontent.h" #include "poppler-page-transition.h" +#include "poppler-converter.h" #include #include @@ -1671,6 +1674,13 @@ QString subject = m_doc->info("Subject"); */ OptContentModel *optionalContentModel(); + /** + Resets the form with the details contained in the \p link. + + \since 24.07 + */ + void applyResetFormsLink(const LinkResetForm &link); + /** Document-level JavaScript scripts. @@ -1679,6 +1689,31 @@ QString subject = m_doc->info("Subject"); */ QStringList scripts() const; + /** + Describes the flags for additional document actions i.e. + for executing document scripts at different events. + This flag is used by additionalAction method to return the + particular Link. + + \since 24.07 + */ + enum DocumentAdditionalActionsType + { + CloseDocument, ///< Performed before closing the document + SaveDocumentStart, ///< Performed before saving the document + SaveDocumentFinish, ///< Performed after saving the document + PrintDocumentStart, ///< Performed before printing the document + PrintDocumentFinish, ///< Performed after printing the document + }; + + /** + Returns the additional action of the given @p type for the document or + @c 0 if no action has been defined. + + \since 24.07 + */ + std::unique_ptr additionalAction(DocumentAdditionalActionsType type) const; + /** The PDF identifiers. @@ -1737,393 +1772,6 @@ QString subject = m_doc->info("Subject"); explicit Document(DocumentData *dataA); }; -class BaseConverterPrivate; -class PSConverterPrivate; -class PDFConverterPrivate; -/** - \brief Base converter. - - This is the base class for the converters. -*/ -class POPPLER_QT6_EXPORT BaseConverter -{ - friend class Document; - -public: - /** - Destructor. - */ - virtual ~BaseConverter(); - - /** Sets the output file name. You must set this or the output device. */ - void setOutputFileName(const QString &outputFileName); - - /** - * Sets the output device. You must set this or the output file name. - */ - void setOutputDevice(QIODevice *device); - - /** - Does the conversion. - - \return whether the conversion succeeded - */ - virtual bool convert() = 0; - - enum Error - { - NoError, - FileLockedError, - OpenOutputError, - NotSupportedInputFileError - }; - - /** - Returns the last error - */ - Error lastError() const; - -protected: - /// \cond PRIVATE - explicit BaseConverter(BaseConverterPrivate &dd); - Q_DECLARE_PRIVATE(BaseConverter) - BaseConverterPrivate *d_ptr; - /// \endcond - -private: - Q_DISABLE_COPY(BaseConverter) -}; - -/** - Converts a PDF to PS - - Sizes have to be in Points (1/72 inch) - - If you are using QPrinter you can get paper size by doing: - \code -QPrinter dummy(QPrinter::PrinterResolution); -dummy.setFullPage(true); -dummy.setPageSize(myPageSize); -width = dummy.width(); -height = dummy.height(); - \endcode -*/ -class POPPLER_QT6_EXPORT PSConverter : public BaseConverter -{ - friend class Document; - -public: - /** - Options for the PS export. - */ - enum PSOption - { - Printing = 0x00000001, ///< The PS is generated for printing purposes - StrictMargins = 0x00000002, - ForceRasterization = 0x00000004, - PrintToEPS = 0x00000008, ///< Output EPS instead of PS - HideAnnotations = 0x00000010 ///< Don't print annotations - }; - Q_DECLARE_FLAGS(PSOptions, PSOption) - - /** - Destructor. - */ - ~PSConverter() override; - - /** Sets the list of pages to print. Mandatory. */ - void setPageList(const QList &pageList); - - /** - Sets the title of the PS Document. Optional - */ - void setTitle(const QString &title); - - /** - Sets the horizontal DPI. Defaults to 72.0 - */ - void setHDPI(double hDPI); - - /** - Sets the vertical DPI. Defaults to 72.0 - */ - void setVDPI(double vDPI); - - /** - Sets the rotate. Defaults to not rotated - */ - void setRotate(int rotate); - - /** - Sets the output paper width. Has to be set. - */ - void setPaperWidth(int paperWidth); - - /** - Sets the output paper height. Has to be set. - */ - void setPaperHeight(int paperHeight); - - /** - Sets the output right margin. Defaults to 0 - */ - void setRightMargin(int marginRight); - - /** - Sets the output bottom margin. Defaults to 0 - */ - void setBottomMargin(int marginBottom); - - /** - Sets the output left margin. Defaults to 0 - */ - void setLeftMargin(int marginLeft); - - /** - Sets the output top margin. Defaults to 0 - */ - void setTopMargin(int marginTop); - - /** - Defines if margins have to be strictly followed (even if that - means changing aspect ratio), or if the margins can be adapted - to keep aspect ratio. - - Defaults to false. - */ - void setStrictMargins(bool strictMargins); - - /** Defines if the page will be rasterized to an image before printing. Defaults to false */ - void setForceRasterize(bool forceRasterize); - - /** - Sets the options for the PS export. - */ - void setPSOptions(PSOptions options); - - /** - The currently set options for the PS export. - - The default flags are: Printing. - */ - PSOptions psOptions() const; - - /** - Sets a function that will be called each time a page is converted. - - The payload belongs to the caller. - */ - void setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload); - - bool convert() override; - -private: - Q_DECLARE_PRIVATE(PSConverter) - Q_DISABLE_COPY(PSConverter) - - explicit PSConverter(DocumentData *document); -}; - -/** - Converts a PDF to PDF (thus saves a copy of the document). -*/ -class POPPLER_QT6_EXPORT PDFConverter : public BaseConverter -{ - friend class Document; - -public: - /** - Options for the PDF export. - */ - enum PDFOption - { - WithChanges = 0x00000001 ///< The changes done to the document are saved as well - }; - Q_DECLARE_FLAGS(PDFOptions, PDFOption) - - /** - Destructor. - */ - ~PDFConverter() override; - - /** - Sets the options for the PDF export. - */ - void setPDFOptions(PDFOptions options); - /** - The currently set options for the PDF export. - */ - PDFOptions pdfOptions() const; - - /** - * Holds data for a new signature - * - Common Name of cert to sign (aka nickname) - * - password for the cert - * - page where to add the signature - * - rect for the signature annotation - * - text that will be shown inside the rect - * - font size and color - * - border width and color - * - background color - * \since 21.01 - */ - class POPPLER_QT6_EXPORT NewSignatureData - { - public: - NewSignatureData(); - ~NewSignatureData(); - NewSignatureData(const NewSignatureData &) = delete; - NewSignatureData &operator=(const NewSignatureData &) = delete; - - QString certNickname() const; - void setCertNickname(const QString &certNickname); - - QString password() const; - void setPassword(const QString &password); - - int page() const; - void setPage(int page); - - QRectF boundingRectangle() const; - void setBoundingRectangle(const QRectF &rect); - - QString signatureText() const; - void setSignatureText(const QString &text); - - /** - * If this text is not empty, the signature representation - * will split in two, with this text on the left and signatureText - * on the right - * - * \since 21.06 - */ - QString signatureLeftText() const; - void setSignatureLeftText(const QString &text); - - /** - * Signature's property Reason. - * - * Default: an empty string. - * - * \since 21.10 - */ - QString reason() const; - void setReason(const QString &reason); - - /** - * Signature's property Location. - * - * Default: an empty string. - * - * \since 21.10 - */ - QString location() const; - void setLocation(const QString &location); - - /** - * Default: 10 - */ - double fontSize() const; - void setFontSize(double fontSize); - - /** - * Default: 20 - * - * \since 21.06 - */ - double leftFontSize() const; - void setLeftFontSize(double fontSize); - - /** - * Default: red - */ - QColor fontColor() const; - void setFontColor(const QColor &color); - - /** - * Default: red - */ - QColor borderColor() const; - void setBorderColor(const QColor &color); - - /** - * border width in points - * - * Default: 1.5 - * - * \since 21.05 - */ - double borderWidth() const; - void setBorderWidth(double width); - - /** - * Default: QColor(240, 240, 240) - */ - QColor backgroundColor() const; - void setBackgroundColor(const QColor &color); - - /** - * Default: QUuid::createUuid().toString() - */ - QString fieldPartialName() const; - void setFieldPartialName(const QString &name); - - /** - * Document owner password (needed if the document that is being signed is password protected) - * - * Default: no password - * - * \since 22.02 - */ - QByteArray documentOwnerPassword() const; - void setDocumentOwnerPassword(const QByteArray &password); - - /** - * Document user password (needed if the document that is being signed is password protected) - * - * Default: no password - * - * \since 22.02 - */ - QByteArray documentUserPassword() const; - void setDocumentUserPassword(const QByteArray &password); - - /** - * Filesystem path to an image file to be used as background - * image for the signature annotation widget. - * - * Default: empty - * - * \since 22.02 - */ - QString imagePath() const; - void setImagePath(const QString &path); - - private: - struct NewSignatureDataPrivate; - NewSignatureDataPrivate *const d; - }; - - /** - Sign PDF at given Annotation / signature form - - \param data new signature data - - \return whether the signing succeeded - - \since 21.01 - */ - bool sign(const NewSignatureData &data); - - bool convert() override; - -private: - Q_DECLARE_PRIVATE(PDFConverter) - Q_DISABLE_COPY(PDFConverter) - - explicit PDFConverter(DocumentData *document); -}; - /** Conversion from PDF date string format to QDateTime */ @@ -2293,7 +1941,5 @@ class POPPLER_QT6_EXPORT MovieObject Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::PainterFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::SearchFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Document::RenderHints) -Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PDFConverter::PDFOptions) -Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PSConverter::PSOptions) #endif diff --git a/qt6/src/poppler-sound.cc b/qt6/src/poppler-sound.cc index 2a33a49..e224ced 100644 --- a/qt6/src/poppler-sound.cc +++ b/qt6/src/poppler-sound.cc @@ -1,6 +1,7 @@ /* poppler-sound.cc: qt interface to poppler * Copyright (C) 2006-2007, Pino Toscano * Copyright (C) 2008, 2018, 2020, Albert Astals Cid + * Copyright (C) 2025, g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,12 +82,14 @@ QByteArray SoundObject::data() const } Stream *stream = m_soundData->m_soundObj->getStream(); - stream->reset(); + if (!stream->reset()) { + return QByteArray {}; + } int dataLen = 0; QByteArray fileArray; int i; while ((i = stream->getChar()) != EOF) { - fileArray[dataLen] = (char)i; + fileArray.append((char)i); ++dataLen; } fileArray.resize(dataLen); diff --git a/qt6/tests/CMakeLists.txt b/qt6/tests/CMakeLists.txt index d5f7184..7675288 100644 --- a/qt6/tests/CMakeLists.txt +++ b/qt6/tests/CMakeLists.txt @@ -22,7 +22,9 @@ macro(QT6_ADD_QTEST exe source) ${source} ) poppler_add_test(${exe} BUILD_QT6_TESTS ${${test_name}_SOURCES}) - add_test(${exe} ${EXECUTABLE_OUTPUT_PATH}/${exe}) + if(BUILD_QT6_TESTS) + add_test(${exe} ${EXECUTABLE_OUTPUT_PATH}/${exe}) + endif() target_link_libraries(${exe} poppler-qt6 Qt6::Widgets Qt6::Test Qt6::Gui) endmacro() @@ -60,6 +62,14 @@ qt6_add_qtest(check_qt6_object check_object.cpp) qt6_add_qtest(check_qt6_stroke_opacity check_stroke_opacity.cpp) qt6_add_qtest(check_qt6_utf_conversion check_utf_conversion.cpp) qt6_add_qtest(check_qt6_outline check_outline.cpp) +qt6_add_qtest(check_qt6_signature_basics check_signature_basics.cpp) +qt6_add_qtest(check_qt6_utf8document check_utf8document.cpp) +qt6_add_qtest(check_qt6_distinguished_name_parser check_distinguished_name_parser.cpp) +qt6_add_qtest(check_qt6_cidfontswidthsbuilder check_cidfontswidthsbuilder.cpp) +qt6_add_qtest(check_qt6_overprint check_overprint.cpp) +if (ENABLE_GPGME) + target_link_libraries(check_qt6_signature_basics Gpgmepp) +endif() if (NOT WIN32) qt6_add_qtest(check_qt6_pagelabelinfo check_pagelabelinfo.cpp) qt6_add_qtest(check_qt6_strings check_strings.cpp) diff --git a/qt6/tests/check_actualtext.cpp b/qt6/tests/check_actualtext.cpp index d2eff79..aaa8914 100644 --- a/qt6/tests/check_actualtext.cpp +++ b/qt6/tests/check_actualtext.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -9,20 +9,25 @@ class TestActualText : public QObject Q_OBJECT public: explicit TestActualText(QObject *parent = nullptr) : QObject(parent) { } + + void checkActualText(Poppler::Document &doc, const QRectF &area, const QString &text); + private slots: void checkActualText1(); void checkActualText2(); - -private: - void checkActualText(Poppler::Document &doc); + void checkActualText2_data(); + void checkAllOrientations(); + void checkAllOrientations_data(); + void checkFakeboldText(); + void checkFakeboldText_data(); }; -void TestActualText::checkActualText(Poppler::Document &doc) +void TestActualText::checkActualText(Poppler::Document &doc, const QRectF &area, const QString &text) { std::unique_ptr page = doc.page(0); QVERIFY(page); - QCOMPARE(page->text(QRectF()), QLatin1String("The slow brown fox jumps over the black dog.")); + QCOMPARE(page->text(area), text); } void TestActualText::checkActualText1() @@ -30,18 +35,136 @@ void TestActualText::checkActualText1() std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(doc); - checkActualText(*doc); + checkActualText(*doc, QRectF {}, QStringLiteral("The slow brown fox jumps over the black dog.")); } void TestActualText::checkActualText2() { + QFETCH(QRectF, area); + QFETCH(QString, text); + QFile file(TESTDATADIR "/unittestcases/WithActualText.pdf"); QVERIFY(file.open(QIODevice::ReadOnly)); std::unique_ptr doc = Poppler::Document::load(&file); QVERIFY(doc); - checkActualText(*doc); + checkActualText(*doc, area, text); +} + +void TestActualText::checkActualText2_data() +{ + QTest::addColumn("area"); + QTest::addColumn("text"); + + // Line bounding box is [100.000 90.720 331.012110 102.350] + + QTest::newRow("full page") << QRectF {} << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("full line") << QRectF { 50.0, 90.0, 290.0, 20.0 } << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("full line [narrow]") << QRectF { 50.0, 95.0, 290.0, 5.0 } << QStringLiteral("The slow brown fox jumps over the black dog."); + QTest::newRow("above line") << QRectF { 50.0, 85.0, 290.0, 10.0 } << QString {}; + QTest::newRow("above line mid") << QRectF { 50.0, 90.0, 290.0, 5.0 } << QString {}; + QTest::newRow("first two words") << QRectF { 50.0, 90.0, 100.0, 20.0 } << QStringLiteral("The slow"); + QTest::newRow("first two words [narrow]") << QRectF { 50.0, 95.0, 100.0, 5.0 } << QStringLiteral("The slow"); + QTest::newRow("first character") << QRectF { 103.0, 95.0, 1.0, 5.0 } << QStringLiteral("T"); + QTest::newRow("last two words") << QRectF { 285.0, 90.0, 100.0, 20.0 } << QStringLiteral("black dog."); + QTest::newRow("last character") << QRectF { 320.0, 90.0, 8.0, 20.0 } << QStringLiteral("g"); + QTest::newRow("middle 'fox'") << QRectF { 190.0, 90.0, 15.0, 20.0 } << QStringLiteral("fox"); + QTest::newRow("middle 'x'") << QRectF { 200.0, 90.0, 5.0, 20.0 } << QStringLiteral("x"); +} + +void TestActualText::checkAllOrientations() +{ + QFETCH(int, pageNr); + QFETCH(QRectF, area); + QFETCH(QString, text); + + QString path { TESTDATADIR "/unittestcases/orientation.pdf" }; + std::unique_ptr doc { Poppler::Document::load(path) }; + QVERIFY(doc); + + std::unique_ptr page { doc->page(pageNr) }; + QVERIFY(page); + + QCOMPARE(page->text(area), text); +} + +void TestActualText::checkAllOrientations_data() +{ + QTest::addColumn("pageNr"); + QTest::addColumn("area"); + QTest::addColumn("text"); + + QTest::newRow("Portrait") << 0 << QRectF {} << QStringLiteral("Portrait"); + QTest::newRow("Landscape") << 1 << QRectF {} << QStringLiteral("Landscape"); + QTest::newRow("Upside down") << 2 << QRectF {} << QStringLiteral("Upside down"); + QTest::newRow("Seacape") << 3 << QRectF {} << QStringLiteral("Seascape"); + + QTest::newRow("Portrait A4 rect") << 0 << QRectF { 0, 0, 595, 842 } << QStringLiteral("Portrait"); + QTest::newRow("Landscape A4 rect") << 1 << QRectF { 0, 0, 842, 595 } << QStringLiteral("Landscape"); + QTest::newRow("Upside down A4 rect") << 2 << QRectF { 0, 0, 595, 842 } << QStringLiteral("Upside down"); + QTest::newRow("Seacape A4 rect") << 3 << QRectF { 0, 0, 842, 595 } << QStringLiteral("Seascape"); + + QTest::newRow("Portrait line rect") << 0 << QRectF { 30, 30, 60, 20 } << QStringLiteral("Portrait"); + QTest::newRow("Landscape line rect") << 1 << QRectF { 790, 30, 20, 80 } << QStringLiteral("Landscape"); + QTest::newRow("Upside down line rect") << 2 << QRectF { 485, 790, 75, 20 } << QStringLiteral("Upside down"); + QTest::newRow("Seacape line rect") << 3 << QRectF { 30, 500, 20, 70 } << QStringLiteral("Seascape"); + + QTest::newRow("Portrait small rect B") << 0 << QRectF { 30, 35, 10, 10 } << QStringLiteral("P"); + QTest::newRow("Portrait small rect E") << 0 << QRectF { 80, 35, 10, 10 } << QStringLiteral("t"); + QTest::newRow("Landscape small rect B") << 1 << QRectF { 800, 30, 10, 10 } << QStringLiteral("L"); + QTest::newRow("Landscape small rect E") << 1 << QRectF { 800, 90, 10, 10 } << QStringLiteral("e"); + QTest::newRow("Upside down small rect B") << 2 << QRectF { 550, 800, 10, 10 } << QStringLiteral("U"); + QTest::newRow("Upside down small rect E") << 2 << QRectF { 485, 800, 10, 10 } << QStringLiteral("n"); + QTest::newRow("Seacape small rect B") << 3 << QRectF { 40, 550, 10, 10 } << QStringLiteral("S"); + QTest::newRow("Seacape small rect E") << 3 << QRectF { 40, 510, 10, 10 } << QStringLiteral("p"); +} + +void TestActualText::checkFakeboldText() +{ + QFETCH(int, pageNr); + QFETCH(QRectF, area); + QFETCH(QString, text); + + QString path { TESTDATADIR "/unittestcases/fakebold.pdf" }; + std::unique_ptr doc { Poppler::Document::load(path) }; + QVERIFY(doc); + + std::unique_ptr page { doc->page(pageNr) }; + QVERIFY(page); + + QCOMPARE(page->text(area), text); +} + +void TestActualText::checkFakeboldText_data() +{ + QTest::addColumn("pageNr"); + QTest::addColumn("area"); + QTest::addColumn("text"); + + QTest::newRow("Upright line 1") << 0 << QRectF { 0, 0, 595, 80 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Upright line 2") << 0 << QRectF { 0, 80, 595, 80 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Upright line 3") << 0 << QRectF { 0, 140, 595, 80 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Upright line 4") << 0 << QRectF { 0, 220, 595, 80 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Upright line 5") << 0 << QRectF { 0, 300, 595, 80 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 90' line 1") << 1 << QRectF { 510, 0, 80, 842 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 90' line 2") << 1 << QRectF { 430, 0, 80, 842 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 90' line 3") << 1 << QRectF { 350, 0, 80, 842 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 90' line 4") << 1 << QRectF { 270, 0, 80, 842 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 90' line 5") << 1 << QRectF { 190, 0, 80, 842 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 180' line 1") << 2 << QRectF { 0, 760, 595, 80 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 180' line 2") << 2 << QRectF { 0, 680, 595, 80 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 180' line 3") << 2 << QRectF { 0, 600, 595, 80 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 180' line 4") << 2 << QRectF { 0, 520, 595, 80 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 180' line 5") << 2 << QRectF { 0, 440, 595, 80 } << QStringLiteral("5 Quoted \"fakebold\" word."); + + QTest::newRow("Rotated 270' line 1") << 3 << QRectF { 20, 0, 80, 842 } << QStringLiteral("1 This is fakebold text."); + QTest::newRow("Rotated 270' line 2") << 3 << QRectF { 100, 0, 80, 842 } << QStringLiteral("2 This is a fakebold word."); + QTest::newRow("Rotated 270' line 3") << 3 << QRectF { 160, 0, 80, 842 } << QStringLiteral("3 The last word is in fakebold."); + QTest::newRow("Rotated 270' line 4") << 3 << QRectF { 240, 0, 80, 842 } << QStringLiteral("4 Hyphenated-fakebold word."); + QTest::newRow("Rotated 270' line 5") << 3 << QRectF { 320, 0, 80, 842 } << QStringLiteral("5 Quoted \"fakebold\" word."); } QTEST_GUILESS_MAIN(TestActualText) diff --git a/qt6/tests/check_annotations.cpp b/qt6/tests/check_annotations.cpp index ca00d47..514dd3e 100644 --- a/qt6/tests/check_annotations.cpp +++ b/qt6/tests/check_annotations.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -35,8 +35,8 @@ void TestAnnotations::checkQColorPrecision() bool precisionOk = true; for (int i = std::numeric_limits::min(); i <= std::numeric_limits::max(); i++) { double normalized = static_cast(i) / static_cast(std::numeric_limits::max()); - const std::unique_ptr serialized = GooString::format("{0:.5f}", normalized); - double deserialized = gatof(serialized->c_str()); + const std::string serialized = GooString::format("{0:.5f}", normalized); + double deserialized = gatof(serialized.c_str()); uint16_t denormalized = std::round(deserialized * std::numeric_limits::max()); if (static_cast(i) != denormalized) { precisionOk = false; diff --git a/qt6/tests/check_attachments.cpp b/qt6/tests/check_attachments.cpp index 319ec4e..74b5511 100644 --- a/qt6/tests/check_attachments.cpp +++ b/qt6/tests/check_attachments.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_cidfontswidthsbuilder.cpp b/qt6/tests/check_cidfontswidthsbuilder.cpp new file mode 100644 index 0000000..a9d1b9e --- /dev/null +++ b/qt6/tests/check_cidfontswidthsbuilder.cpp @@ -0,0 +1,115 @@ +//======================================================================== +// +// check_cidfontswidthsbuilder.cpp +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +#include "CIDFontsWidthsBuilder.h" + +#include + +class TestCIDFontsWidthsBuilder : public QObject +{ + Q_OBJECT +public: + using QObject::QObject; +private Q_SLOTS: + void testEmpty(); + void testSingleList(); + void testSingleRange(); + void testSimpleSequence(); +}; + +void TestCIDFontsWidthsBuilder::testEmpty() +{ + CIDFontsWidthsBuilder b; + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 0); +} + +static bool compare(const CIDFontsWidthsBuilder::Segment &segment1, const CIDFontsWidthsBuilder::Segment &segment2) +{ + return std::visit( + [](const auto &s1, const auto &s2) { + using T1 = std::decay_t; + using T2 = std::decay_t; + if constexpr (!std::is_same_v) { + return false; + } else if constexpr (std::is_same_v) { + return s1.first == s2.first && s1.widths == s2.widths; + } else if constexpr (std::is_same_v) { + return s1.first == s2.first && s1.last == s2.last && s1.width == s2.width; + } else { + return false; + } + }, + segment1, segment2); +} + +void TestCIDFontsWidthsBuilder::testSingleList() +{ + CIDFontsWidthsBuilder b; + b.addWidth(0, 10); + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 1); + auto segment0 = CIDFontsWidthsBuilder::ListSegment { 0, { 10 } }; + QVERIFY(compare(segments[0], segment0)); +} + +void TestCIDFontsWidthsBuilder::testSingleRange() +{ + CIDFontsWidthsBuilder b; + b.addWidth(0, 10); + b.addWidth(1, 10); + b.addWidth(2, 10); + b.addWidth(3, 10); + b.addWidth(4, 10); + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 1); + auto segment0 = CIDFontsWidthsBuilder::RangeSegment { 0, 4, 10 }; + QVERIFY(compare(segments[0], segment0)); +} + +void TestCIDFontsWidthsBuilder::testSimpleSequence() +{ + CIDFontsWidthsBuilder b; + for (int i = 0; i < 2; i++) { // repeat to verify that takeSegments resets + b.addWidth(0, 10); + b.addWidth(1, 10); + b.addWidth(2, 10); + b.addWidth(3, 10); + b.addWidth(4, 10); + b.addWidth(5, 20); + b.addWidth(6, 21); + b.addWidth(7, 21); + b.addWidth(8, 20); + b.addWidth(9, 10); + b.addWidth(10, 10); + b.addWidth(11, 10); + b.addWidth(12, 10); + b.addWidth(13, 10); + b.addWidth(14, 20); + b.addWidth(15, 21); + b.addWidth(16, 21); + b.addWidth(17, 20); + b.addWidth(19, 20); + auto segments = b.takeSegments(); + QCOMPARE(segments.size(), 5); + auto segment0 = CIDFontsWidthsBuilder::RangeSegment { 0, 4, 10 }; + QVERIFY(compare(segments[0], segment0)); + auto segment1 = CIDFontsWidthsBuilder::ListSegment { 5, { 20, 21, 21, 20 } }; + QVERIFY(compare(segments[1], segment1)); + auto segment2 = CIDFontsWidthsBuilder::RangeSegment { 9, 13, 10 }; + QVERIFY(compare(segments[2], segment2)); + auto segment3 = CIDFontsWidthsBuilder::ListSegment { 14, { 20, 21, 21, 20 } }; + QVERIFY(compare(segments[3], segment3)); + auto segment4 = CIDFontsWidthsBuilder::ListSegment { 19, { 20 } }; + QVERIFY(compare(segments[4], segment4)); + } +} + +QTEST_GUILESS_MAIN(TestCIDFontsWidthsBuilder); +#include "check_cidfontswidthsbuilder.moc" diff --git a/qt6/tests/check_dateConversion.cpp b/qt6/tests/check_dateConversion.cpp index d675a40..331dcf3 100644 --- a/qt6/tests/check_dateConversion.cpp +++ b/qt6/tests/check_dateConversion.cpp @@ -1,4 +1,4 @@ -#include +#include Q_DECLARE_METATYPE(QDate) Q_DECLARE_METATYPE(QTime) diff --git a/qt6/tests/check_distinguished_name_parser.cpp b/qt6/tests/check_distinguished_name_parser.cpp new file mode 100644 index 0000000..2c2e4f3 --- /dev/null +++ b/qt6/tests/check_distinguished_name_parser.cpp @@ -0,0 +1,161 @@ +//======================================================================== +// +// check_distinguished_name_parser.h +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== +#include "DistinguishedNameParser.h" + +#include +#include + +class TestDistinguishedNameParser : public QObject +{ + Q_OBJECT +public: + explicit TestDistinguishedNameParser(QObject *parent = nullptr) : QObject(parent) { } +private slots: + // The big set of input/output. Several of the helper functions can be tested independently + void testParser(); + void testParser_data(); + + void testRemoveLeadingSpaces(); + void testRemoveLeadingSpaces_data(); + + void testRemoveTrailingSpaces(); + void testRemoveTrailingSpaces_data(); + + void testParseHexString(); + void testParseHexString_data(); +}; + +void TestDistinguishedNameParser::testParser() +{ + QFETCH(std::string, inputData); + QFETCH(DN::Result, expectedResult); + + auto result = DN::parseString(inputData); + QCOMPARE(result, expectedResult); +} + +void TestDistinguishedNameParser::testParser_data() +{ + QTest::addColumn("inputData"); + QTest::addColumn("expectedResult"); + + QTest::newRow("empty") << std::string {} << DN::Result {}; + QTest::newRow("CN=Simple") << std::string { "CN=Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Name with spaces") << std::string { "CN=Name with spaces" } << DN::Result { { "CN", "Name with spaces" } }; + QTest::newRow("CN=Simple,O=Silly") << std::string { "CN=Simple,O=Silly" } << DN::Result { { "CN", "Simple" }, { "O", "Silly" } }; + QTest::newRow("CN=Steve Kille,O=Isode Limited,C=GB") << std::string { "CN=Steve Kille,O=Isode Limited,C=GB" } << DN::Result { { "CN", "Steve Kille" }, { "O", "Isode Limited" }, { "C", "GB" } }; + QTest::newRow("CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US") + << std::string { "CN=some.user@example.com, O=MyCompany, L=San Diego,ST=California, C=US" } << DN::Result { { "CN", "some.user@example.com" }, { "O", "MyCompany" }, { "L", "San Diego" }, { "ST", "California" }, { "C", "US" } }; + QTest::newRow("Multi valued") << std::string { "OU=Sales+CN=J. Smith,O=Widget Inc.,C=US" } + << DN::Result { { "OU", "Sales" }, { "CN", "J. Smith" }, { "O", "Widget Inc." }, { "C", "US" } }; // This is technically wrong, but probably good enough for now + QTest::newRow("Escaping comma") << std::string { "CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB" } << DN::Result { { "CN", "L. Eagle" }, { "O", "Sue, Grabbit and Runn" }, { "C", "GB" } }; + QTest::newRow("Escaped trailing space") << std::string { "CN=Trailing space\\ " } << DN::Result { { "CN", "Trailing space " } }; + QTest::newRow("Escaped quote") << std::string { "CN=Quotation \\\" Mark" } << DN::Result { { "CN", "Quotation \" Mark" } }; + + QTest::newRow("CN=Simple with escaping") << std::string { R"(CN=S\69mpl\65\7A)" } << DN::Result { { "CN", "Simplez" } }; + QTest::newRow(R"(SN=Lu\C4\8Di\C4\87)") << std::string { R"(SN=Lu\C4\8Di\C4\87)" } << DN::Result { { "SN", "Lučić" } }; + QTest::newRow("CN=\"Quoted name\"") << std::string { "CN=\"Quoted name\"" } << DN::Result { { "CN", "Quoted name" } }; + QTest::newRow("CN=\" Leading and trailing spacees \"") << std::string { "CN=\" Leading and trailing spaces \"" } << DN::Result { { "CN", " Leading and trailing spaces " } }; + QTest::newRow("Comma in quotes") << std::string { "CN=\"Comma, inside\"" } << DN::Result { { "CN", "Comma, inside" } }; + QTest::newRow("forbidden chars in quotes") << std::string { R"(CN="Forbidden !@#$%&*()<>[]{},.?/\| chars")" } << DN::Result { { "CN", "Forbidden !@#$%&*()<>[]{},.?/\\| chars" } }; + QTest::newRow("Quoted quotation") << std::string { R"(CN="Quotation \" Mark")" } << DN::Result { { "CN", "Quotation \" Mark" } }; + QTest::newRow("Quoted quotation") << std::string { R"(CN="Quotation \" Mark\" Multiples")" } << DN::Result { { "CN", "Quotation \" Mark\" Multiples" } }; + + QTest::newRow("frompdf1") << std::string { "2.5.4.97=#5553742D49644E722E20444520313233343735323233,CN=TeleSec PKS eIDAS QES CA 5,O=Deutsche Telekom AG,C=DE" } + << DN::Result { { "2.5.4.97", "USt-IdNr. DE 123475223" }, { "CN", "TeleSec PKS eIDAS QES CA 5" }, { "O", "Deutsche Telekom AG" }, { "C", "DE" } }; + QTest::newRow("frompdf2") << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,2.5.4.42=#5765726E6572,2.5.4.4=#4B6F6368,C=DE" } + << DN::Result { { "SerialNumber", "4" }, { "CN", "Koch, Werner" }, { "GN", "Werner" }, { "SN", "Koch" }, { "C", "DE" } }; + QTest::newRow("frompdf2a") << std::string { "2.5.4.5=#34,CN=Koch\\, Werner,oid.2.5.4.42=#5765726E6572,OID.2.5.4.4=#4B6F6368,C=DE" } + << DN::Result { { "SerialNumber", "4" }, { "CN", "Koch, Werner" }, { "GN", "Werner" }, { "SN", "Koch" }, { "C", "DE" } }; + + // weird spacing + QTest::newRow("CN =Simple") << std::string { "CN =Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN= Simple") << std::string { "CN= Simple" } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple ") << std::string { "CN=Simple " } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple,") << std::string { "CN=Simple," } << DN::Result { { "CN", "Simple" } }; + QTest::newRow("CN=Simple, O=Silly") << std::string { "CN=Simple, O=Silly" } << DN::Result { { "CN", "Simple" }, { "O", "Silly" } }; + + // various malformed + QTest::newRow("CN=Simple\\") << std::string { "CN=Simple\\" } << DN::Result {}; + QTest::newRow("CN=") << std::string { "CN=" } << DN::Result {}; + QTest::newRow("CN=Simple\\X") << std::string { "CN=Simple\\X" } << DN::Result {}; + QTest::newRow("CN=Simple, O") << std::string { "CN=Simple, O" } << DN::Result {}; + QTest::newRow("CN=Sim\"ple") << std::string { "CN=Sim\"ple, O" } << DN::Result {}; + QTest::newRow("CN=Simple\\a") << std::string { "CN=Simple\\a" } << DN::Result {}; + QTest::newRow("=Simple") << std::string { "=Simple" } << DN::Result {}; + QTest::newRow("CN=\"Simple") << std::string { "CN=\"Simple" } << DN::Result {}; + QTest::newRow("CN=\"Simple") << std::string { "CN=\"Simple\\" } << DN::Result {}; + QTest::newRow("unquoted quotation in quotation") << std::string { R"("CN="Quotation " Mark")" } << DN::Result {}; +} + +void TestDistinguishedNameParser::testRemoveLeadingSpaces() +{ + QFETCH(std::string, input); + QFETCH(std::string, expectedOutput); + + auto result = DN::detail::removeLeadingSpaces(input); + QCOMPARE(result, expectedOutput); +} +void TestDistinguishedNameParser::testRemoveLeadingSpaces_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedOutput"); + + QTest::newRow("Empty") << std::string {} << std::string {}; + QTest::newRow("No leading spaces") << std::string { "horse" } << std::string { "horse" }; + QTest::newRow("Some spaces") << std::string { " horse" } << std::string { "horse" }; + QTest::newRow("Some leading and trailing") << std::string { " horse " } << std::string { "horse " }; +} + +void TestDistinguishedNameParser::testRemoveTrailingSpaces() +{ + QFETCH(std::string, input); + QFETCH(std::string, expectedOutput); + + auto result = DN::detail::removeTrailingSpaces(input); + QCOMPARE(result, expectedOutput); +} +void TestDistinguishedNameParser::testRemoveTrailingSpaces_data() +{ + QTest::addColumn("input"); + QTest::addColumn("expectedOutput"); + + QTest::newRow("Empty") << std::string {} << std::string {}; + QTest::newRow("No leading spaces") << std::string { "horse" } << std::string { "horse" }; + QTest::newRow("Some spaces") << std::string { "horse " } << std::string { "horse" }; + QTest::newRow("Some leading and trailing") << std::string { " horse " } << std::string { " horse" }; +} + +void TestDistinguishedNameParser::testParseHexString() +{ + QFETCH(std::string, input); + QFETCH(std::optional, expectedOutput); + + auto result = DN::detail::parseHexString(input); + QCOMPARE(result, expectedOutput); +} + +void TestDistinguishedNameParser::testParseHexString_data() +{ + QTest::addColumn("input"); + QTest::addColumn>("expectedOutput"); + + QTest::newRow("4") << std::string { "34" } << std::optional("4"); + QTest::newRow("Koch") << std::string { "4B6F6368" } << std::optional("Koch"); + QTest::newRow("USt-IdNr. DE 123475223") << std::string { "5553742D49644E722E20444520313233343735323233" } << std::optional("USt-IdNr. DE 123475223"); + + // various baddies + QTest::newRow("empty") << std::string {} << std::optional {}; + QTest::newRow("FFF") << std::string { "FFF" } << std::optional {}; + QTest::newRow("F") << std::string { "F" } << std::optional {}; + QTest::newRow("XX") << std::string { "XX" } << std::optional {}; +} + +QTEST_GUILESS_MAIN(TestDistinguishedNameParser); +#include "check_distinguished_name_parser.moc" diff --git a/qt6/tests/check_fonts.cpp b/qt6/tests/check_fonts.cpp index a3f0f91..77b8f7e 100644 --- a/qt6/tests/check_fonts.cpp +++ b/qt6/tests/check_fonts.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_forms.cpp b/qt6/tests/check_forms.cpp index 50f2f89..79c9204 100644 --- a/qt6/tests/check_forms.cpp +++ b/qt6/tests/check_forms.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -226,7 +226,7 @@ void TestForms::testSetAppearanceText() Poppler::FormFieldText *fft = static_cast(field.get()); - const QString textToSet = "HOLA" + fft->name(); + const QString textToSet = QStringLiteral("HOLA") + fft->name(); fft->setAppearanceText(textToSet); Dict *dict = Poppler::FormFieldData::getFormWidget(fft)->getObj()->getDict(); diff --git a/qt6/tests/check_goostring.cpp b/qt6/tests/check_goostring.cpp index cfb14c2..110bf17 100644 --- a/qt6/tests/check_goostring.cpp +++ b/qt6/tests/check_goostring.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "goo/GooString.h" @@ -64,60 +64,61 @@ void TestGooString::testInsert() void TestGooString::testFormat() { { - const std::unique_ptr goo(GooString::format("{0:d},{1:x}", 1, 0xF)); - QCOMPARE(goo->c_str(), "1,f"); + const std::string goo(GooString::format("{0:d},{1:x}", 1, 0xF)); + QCOMPARE(goo.c_str(), "1,f"); } { - const std::unique_ptr goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA)); - QCOMPARE(goo->c_str(), "10,a,A,12,1010, "); + const std::string goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b},{0:w}", 0xA)); + QCOMPARE(goo.c_str(), "10,a,A,12,1010, "); } { - const std::unique_ptr goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); - QCOMPARE(goo->c_str(), "-10,-a,-A,-12,-1010"); + const std::string goo(GooString::format("{0:d},{0:x},{0:X},{0:o},{0:b}", -0xA)); + QCOMPARE(goo.c_str(), "-10,-a,-A,-12,-1010"); } { - const std::unique_ptr goo(GooString::format("{0:c}{1:c}{2:c}{3:c}", 'T', (char)'E', (short)'S', (int)'T')); - QCOMPARE(goo->c_str(), "TEST"); + const std::string goo(GooString::format("{0:c}{1:c}{2:c}{3:c}", 'T', (char)'E', (short)'S', (int)'T')); + QCOMPARE(goo.c_str(), "TEST"); - const std::unique_ptr goo2(GooString::format("{0:s} {1:t}", "TEST", goo.get())); - QCOMPARE(goo2->c_str(), "TEST TEST"); + GooString gooGoo(goo); + const std::string goo2(GooString::format("{0:s} {1:t}", "TEST", &gooGoo)); + QCOMPARE(goo2.c_str(), "TEST TEST"); } { - const std::unique_ptr goo(GooString::format("{0:ud} {1:d} {2:d}", UINT_MAX, INT_MAX, INT_MIN)); + const std::string goo(GooString::format("{0:ud} {1:d} {2:d}", UINT_MAX, INT_MAX, INT_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(UINT_MAX).arg(INT_MAX).arg(INT_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr goo(GooString::format("{0:uld} {1:ld} {2:ld}", ULONG_MAX, LONG_MAX, LONG_MIN)); + const std::string goo(GooString::format("{0:uld} {1:ld} {2:ld}", ULONG_MAX, LONG_MAX, LONG_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULONG_MAX).arg(LONG_MAX).arg(LONG_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr goo(GooString::format("{0:ulld} {1:lld} {2:lld}", ULLONG_MAX, LLONG_MAX, LLONG_MIN)); + const std::string goo(GooString::format("{0:ulld} {1:lld} {2:lld}", ULLONG_MAX, LLONG_MAX, LLONG_MIN)); const QByteArray expected = QStringLiteral("%1 %2 %3").arg(ULLONG_MAX).arg(LLONG_MAX).arg(LLONG_MIN).toLatin1(); - QCOMPARE(goo->c_str(), expected.constData()); + QCOMPARE(goo.c_str(), expected.constData()); } { - const std::unique_ptr gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012)); - const std::unique_ptr gooF(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1.f, .012f)); - QCOMPARE(gooD->c_str(), "1.0 1 1 | 0.0 0 0.01"); - QCOMPARE(gooF->c_str(), "1.0 1 1 | 0.0 0 0.01"); + const std::string gooD(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1., .012)); + const std::string gooF(GooString::format("{0:.1f} {0:.1g} {0:.1gs} | {1:.1f} {1:.1g} {1:.1gs}", 1.f, .012f)); + QCOMPARE(gooD.c_str(), "1.0 1 1 | 0.0 0 0.01"); + QCOMPARE(gooF.c_str(), "1.0 1 1 | 0.0 0 0.01"); } { - const std::unique_ptr goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); - QCOMPARE(goo->c_str(), "0.0120 0.012 0.012"); + const std::string goo(GooString::format("{0:.4f} {0:.4g} {0:.4gs}", .012)); + QCOMPARE(goo.c_str(), "0.0120 0.012 0.012"); } { - const std::unique_ptr goo(GooString::format("{{ SomeText {0:d} }}", 1)); - QCOMPARE(goo->c_str(), "{ SomeText 1 }"); + const std::string goo(GooString::format("{{ SomeText {0:d} }}", 1)); + QCOMPARE(goo.c_str(), "{ SomeText 1 }"); } { - const std::unique_ptr goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); - QCOMPARE(goo->c_str(), "{{ { SomeText 2"); + const std::string goo(GooString::format("{{{{ {{ SomeText {0:d}", 2)); + QCOMPARE(goo.c_str(), "{{ { SomeText 2"); } { - const std::unique_ptr goo(GooString::format("SomeText {0:d} }} }}}}", 3)); - QCOMPARE(goo->c_str(), "SomeText 3 } }}"); + const std::string goo(GooString::format("SomeText {0:d} }} }}}}", 3)); + QCOMPARE(goo.c_str(), "SomeText 3 } }}"); } } diff --git a/qt6/tests/check_internal_outline.cpp b/qt6/tests/check_internal_outline.cpp index c12b604..05db72f 100644 --- a/qt6/tests/check_internal_outline.cpp +++ b/qt6/tests/check_internal_outline.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include "Outline.h" #include "PDFDoc.h" @@ -56,10 +57,10 @@ void TestInternalOutline::testCreateOutline() static std::string getTitle(const OutlineItem *item) { - const Unicode *u = item->getTitle(); + const std::vector &u = item->getTitle(); std::string s; - for (int i = 0; i < item->getTitleLength(); i++) { - s.append(1, (char)u[i]); + for (const auto &c : u) { + s.append(1, (char)(c)); } return s; } diff --git a/qt6/tests/check_lexer.cpp b/qt6/tests/check_lexer.cpp index 9c60962..8b7a46f 100644 --- a/qt6/tests/check_lexer.cpp +++ b/qt6/tests/check_lexer.cpp @@ -1,4 +1,4 @@ -#include +#include #include "Object.h" #include "Lexer.h" diff --git a/qt6/tests/check_links.cpp b/qt6/tests/check_links.cpp index 04075c5..c3fcf0d 100644 --- a/qt6/tests/check_links.cpp +++ b/qt6/tests/check_links.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -80,6 +80,10 @@ void TestLinks::checkDests_xr02() dest = doc->linkDestination(QStringLiteral("section.3")); QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc.get())); QVERIFY(isDestinationValid_name(dest.get())); + // Better check that a link that should be in the middle of the sorted names does not give one of the neighboring ones + dest = doc->linkDestination(QStringLiteral("section.15")); + QVERIFY(!isDestinationValid_pageNumber(dest.get(), doc.get())); + QVERIFY(isDestinationValid_name(dest.get())); } void TestLinks::checkDocumentURILink() diff --git a/qt6/tests/check_metadata.cpp b/qt6/tests/check_metadata.cpp index e500800..53098cf 100644 --- a/qt6/tests/check_metadata.cpp +++ b/qt6/tests/check_metadata.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_object.cpp b/qt6/tests/check_object.cpp index 08a7f75..cadc006 100644 --- a/qt6/tests/check_object.cpp +++ b/qt6/tests/check_object.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include "poppler/Object.h" diff --git a/qt6/tests/check_optcontent.cpp b/qt6/tests/check_optcontent.cpp index 5f9294b..918b3cb 100644 --- a/qt6/tests/check_optcontent.cpp +++ b/qt6/tests/check_optcontent.cpp @@ -1,4 +1,4 @@ -#include +#include #include "PDFDoc.h" #include "GlobalParams.h" @@ -83,7 +83,7 @@ void TestOptionalContent::checkIsVisible() PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); - OCGs *ocgs = doc->getOptContentConfig(); + const OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); @@ -155,7 +155,7 @@ void TestOptionalContent::checkVisibilitySetting() PDFDoc *doc = new PDFDoc(std::make_unique(TESTDATADIR "/unittestcases/vis_policy_test.pdf")); QVERIFY(doc); - OCGs *ocgs = doc->getOptContentConfig(); + const OCGs *ocgs = doc->getOptContentConfig(); QVERIFY(ocgs); XRef *xref = doc->getXRef(); @@ -164,7 +164,7 @@ void TestOptionalContent::checkVisibilitySetting() // In this test, both Ref(21,0) and Ref(28,0) start On, // based on the file settings - Object ref21obj({ 21, 0 }); + Object ref21obj(Ref { 21, 0 }); Ref ref21 = ref21obj.getRef(); OptionalContentGroup *ocgA = ocgs->findOcgByRef(ref21); QVERIFY(ocgA); @@ -172,7 +172,7 @@ void TestOptionalContent::checkVisibilitySetting() QVERIFY((ocgA->getName()->cmp("A")) == 0); QCOMPARE(ocgA->getState(), OptionalContentGroup::On); - Object ref28obj({ 28, 0 }); + Object ref28obj(Ref { 28, 0 }); Ref ref28 = ref28obj.getRef(); OptionalContentGroup *ocgB = ocgs->findOcgByRef(ref28); QVERIFY(ocgB); diff --git a/qt6/tests/check_outline.cpp b/qt6/tests/check_outline.cpp index 8f7383e..f497968 100644 --- a/qt6/tests/check_outline.cpp +++ b/qt6/tests/check_outline.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_overprint.cpp b/qt6/tests/check_overprint.cpp new file mode 100644 index 0000000..c70310d --- /dev/null +++ b/qt6/tests/check_overprint.cpp @@ -0,0 +1,38 @@ +#include + +#include + +#include + +class TestOverprint : public QObject +{ + Q_OBJECT +public: + explicit TestOverprint(QObject *parent = nullptr) : QObject(parent) { } +private slots: + void checkOverprintImageRendering(); +}; + +void TestOverprint::checkOverprintImageRendering() +{ + std::unique_ptr doc = Poppler::Document::load(TESTDATADIR "/tests/mask-seams.pdf"); + QVERIFY(doc); + + doc->setRenderHint(Poppler::Document::OverprintPreview, true); + + std::unique_ptr page = doc->page(0); + QVERIFY(page); + + constexpr int width = 600; + constexpr int height = 400; + + QImage img = page->renderToImage(300.0, 300.0, 0, 0, width, height); + QCOMPARE(img.format(), QImage::Format_RGB32); + QCOMPARE(img.width(), width); + QCOMPARE(img.height(), height); + QCOMPARE(img.bytesPerLine(), width * 4); + QCOMPARE(img.sizeInBytes(), width * height * 4); +} + +QTEST_GUILESS_MAIN(TestOverprint) +#include "check_overprint.moc" diff --git a/qt6/tests/check_pagelabelinfo.cpp b/qt6/tests/check_pagelabelinfo.cpp index a8e0f9e..3098bbf 100644 --- a/qt6/tests/check_pagelabelinfo.cpp +++ b/qt6/tests/check_pagelabelinfo.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -31,7 +31,7 @@ void TestPageLabelInfo::testFromDecimal() void TestPageLabelInfo::testFromDecimalUnicode() { std::unique_ptr str(Poppler::QStringToUnicodeGooString(QString::fromLocal8Bit("2342"))); - const auto res = fromDecimal(str->toStr(), str->hasUnicodeMarker()); + const auto res = fromDecimal(str->toStr(), hasUnicodeByteOrderMark(str->toStr())); QCOMPARE(res.first, 2342); QCOMPARE(res.second, true); } diff --git a/qt6/tests/check_pagelayout.cpp b/qt6/tests/check_pagelayout.cpp index e17b293..a4d9513 100644 --- a/qt6/tests/check_pagelayout.cpp +++ b/qt6/tests/check_pagelayout.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_pagemode.cpp b/qt6/tests/check_pagemode.cpp index f20548d..0c4551b 100644 --- a/qt6/tests/check_pagemode.cpp +++ b/qt6/tests/check_pagemode.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_password.cpp b/qt6/tests/check_password.cpp index 24b6f4f..b788fd0 100644 --- a/qt6/tests/check_password.cpp +++ b/qt6/tests/check_password.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -16,6 +16,7 @@ private slots: void password3(); void password4(); void password4b(); + void password5(); }; // BUG:4557 @@ -87,5 +88,14 @@ void TestPassword::password4b() QVERIFY(!doc->isLocked()); } +void TestPassword::password5() +{ + std::unique_ptr doc = Poppler::Document::load(QString::fromUtf8(TESTDATADIR "/unittestcases/PasswordEncryptedReconstructed.pdf")); + QVERIFY(doc); + QVERIFY(doc->isLocked()); + QVERIFY(!doc->unlock("", "test")); + QVERIFY(!doc->isLocked()); +} + QTEST_GUILESS_MAIN(TestPassword) #include "check_password.moc" diff --git a/qt6/tests/check_permissions.cpp b/qt6/tests/check_permissions.cpp index fde1916..ecbb9f9 100644 --- a/qt6/tests/check_permissions.cpp +++ b/qt6/tests/check_permissions.cpp @@ -1,4 +1,4 @@ -#include +#include #include diff --git a/qt6/tests/check_search.cpp b/qt6/tests/check_search.cpp index ede2d0c..384088f 100644 --- a/qt6/tests/check_search.cpp +++ b/qt6/tests/check_search.cpp @@ -1,4 +1,4 @@ -#include +#include #include @@ -348,10 +348,17 @@ void TestSearch::testAcrossLinesSearch() QCOMPARE(page0->search(str6, l, t, r, b, direction, mode1), true); QCOMPARE(page0->search(str6, l, t, r, b, direction, mode2), true); QCOMPARE(page0->search(str6, l, t, r, b, direction, mode2W), true); + // Check for the case when next line falls in next paragraph. Issue #1475 + const QString across_block = QString::fromUtf8("emacs jose"); // clazy:exclude=qstring-allocations + QCOMPARE(page0->search(across_block, l, t, r, b, direction, empty), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode0), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode1), false); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode2), true); + QCOMPARE(page0->search(across_block, l, t, r, b, direction, mode2W), true); // Now for completeness, we will match the full text of two lines - const QString full2lines = QString::fromUtf8( - "Las pruebas se practicarán en vista pública, si bien, excepcionalmente, el Tribunal podrá acordar, mediante providencia, que determinadas pruebas se celebren fuera del acto de juicio"); // clazy:exclude=qstring-allocations + const QString full2lines = QString::fromUtf8( // clazy:exclude=qstring-allocations + "Las pruebas se practicarán en vista pública, si bien, excepcionalmente, el Tribunal podrá acordar, mediante providencia, que determinadas pruebas se celebren fuera del acto de juicio"); QCOMPARE(page->search(full2lines, l, t, r, b, direction, mode0), true); QCOMPARE(page->search(full2lines, l, t, r, b, direction, mode1), true); QCOMPARE(page->search(full2lines, l, t, r, b, direction, mode2), true); diff --git a/qt6/tests/check_signature_basics.cpp b/qt6/tests/check_signature_basics.cpp new file mode 100644 index 0000000..4e8fd5a --- /dev/null +++ b/qt6/tests/check_signature_basics.cpp @@ -0,0 +1,325 @@ +//======================================================================== +// +// check_signature_basics.cpp +// +// This file is licensed under the GPLv2 or later +// +// Copyright 2023-2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +//======================================================================== + +// Simple tests of reading signatures +// +// Note that this does not check the actual validity because +// that will have an expiry date, and adding time bombs to unit tests is +// probably not a good idea. +#include +#include +#include "PDFDoc.h" +#include "GlobalParams.h" +#include "SignatureInfo.h" +#include "CryptoSignBackend.h" +#include "config.h" +#ifdef ENABLE_GPGME +# include "GPGMECryptoSignBackend.h" +# include +#endif + +#ifdef ENABLE_GPGME +const auto pubkey = R"ZxZ( +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBEqHjTcBEADIOb63+TNpG4hwyueK6i09QXAlBkLcjHAX2cyLRuHydfSgOlyQ +17TvZeNrTGg8svP4PWMgeKrnWFeq/PdNsns8omUI4TfmA7zqukRF+DeirAk/sG3d +s/nVqJfjZ9pz7vMexHV1C+OqmWc04OX6y7lAyKOGmfSfu2EVyfYHReyb/epZNU32 +xIWv3lJUE0M+PxsW3VxbR6GBXU/WgAdAXGTPY53aen84GbxUGTKdwo5ULdhEJzkK +1OsYV0BhYUI1QanldDn1EPcYUS7MItLEtS8oSZkqBh5SUsEB8a9a+z0+1Wm0ZKEE +Z3+802WAfG9DQ6WdpE7AgxrjzWnLHCizwbHNYbnrzGklc2y0RxZ2vW2Z0z4KXXeh +h24R2Bas1dWzgfUlqevyRXcqpS5J5gxYqGTKEYBgbWgohXzD/pqo+KNcfGICV1O9 +CgUj1G/d50+5ap9jgsnwUZjDp+uahDKCS34uLs+BGrzDEwGKKqs1Xh70hC6tJU0h +wDcgJCNXc5vdVxv2wpfZ4wuN6In1za5h/z+RYCiJKpjs82Ux3BPn41fxggTGe62Z +htWWVh1lwyr/OHKo+t745lS12AF9BHlpP9JPqrCyfrk5TbWApfS8f5nGhVIqsq0W +APt1mUS+NVe98g3iA4if9HM1kRmHfA2ePj2GMonqgQZN2ebOmtybWCk+4QARAQAB +tB5TdW5lIFZ1b3JlbGEgPHN1bmVAdnVvcmVsYS5kaz6JAjoEEwECACQCGy8CHgEC +F4AFCwkIBwMFFQoJCAsFFgIDAQAFAkqvGHsCGQEACgkQGjB2XfHw0+2JtQ//Y4Ij +OtZ5q6JXNRudnnhMuXtDVO6lwfpxMcTnc2kGjk7pMW1sCyGodQHabcc8OjYwf2jR +6aSoUWU+atF66Bc7w+xtPahzR/B86bWdTjGdcwDBMMgx/j8rraFGm4WhI+Bgqpx0 +bH/tgK+AAqVW1AN9wFi2kYSna03dA+NP1m9W5ldXeKWsgaxg4g2rs/pWQqxbvn/L +NiVw3vaOTnXoXGvcasl7yBIrRNAI7nvBGsacJpByqppTaXnVOMQkP+dEqS50l2R5 +WKwb/aGm+hWarUzxHwKdk/IYWLx5oKR0IXGVNbKHaBWFqSI3oZMCKHChTdEMCenH +VmKLq6GwHWQOtPiCLNJIcev3xKCqfbLezozjRT7fs53dSd/XXFAXsNiEmIQmugYH +LiJxlIjgvJnPJQjco8UzDDq2x5BP9+X31CuYXf/JhntkxGsVC3LE7hOp82Or7yz+ +Rzv8/hn7D7ePZ37tBiEYYMI8yM2H5py18YEzO1oaauv7lkGccxi2DTsZLcw0g0pZ +E0bdKjggA9+CZkFSwLUe5D8/JvY+iidPegf023B492YfqgBfuzcOx1J1JolDTvvM +FXwMoDwCFx6TriehkONB20re8YS/5PtHsa49JHv5AGtuOVKo5yc+apBpussuAPbk +FM6cTZ8TjNq3NL8w69CnhOTexmleIlnhU8SKPJK0HlN1bmUgVnVvcmVsYSA8c3Vu +ZUBkZWJpYW4ub3JnPokCNwQTAQIAIQIbLwIeAQIXgAUCSq8YagULCQgHAwUVCgkI +CwUWAgMBAAAKCRAaMHZd8fDT7S+HEACK3XXLPbREwML/IgVGOJ/67+6O/xDJt5dx +zEllkLh0l7+MM58AhwVN7BPI/QvW9QOfvgadZvDHEM4jQVkpmZpsTgrsH1mypmOO +Iqvf4Ko/m59CNgv0KsvrAZ9GYU9317DzW2pAJxJ4E3zE2GgrCHcnGA598hpVX1s4 +gOLxE16L8rCOzDGDY7NeLppkN232Zm3sy05EvVOY+wZkhGhywwSaPzJA7z8xVnUy +SMimYjm8xLAu5bI1LMjRGfkGlz+TruHB5xhtJZUDd2D+42nX02vT7z8sWUJSSpIV +ZSOOgxeJ298W6pWuDBWQ/aUsc/tQSirpnz7s6u5vhT5toO+i+0jHVtXvt2aQ69RX +tjpNS1LW0C4yMFpncI3FPWvdJp+hpPRol5cpP1FssvT1GYY3ftIhLNY4bLoG4Zjd +bBn/BOaJovqq19ZFsxDLprwZT1rE3idn6YKHyuGzw9666gebxr6GUcE6nz4vn37n +AZzqd696OSxp8lhmilloO+bNKelEBF6th/XaGoKOUgqBP7ScW0oPqA8UN5clPDF2 +WdhL7cyL8JvGcFcgJsfSvoGhZvhM8u8UMozIn+Ve6vpfudLHacuEC9xg0ME5ZHBO +g6jRt0hBfz06wqgxnUQ/W57yTnYSjw99O3a4hcbbM0pon7tvTCVMTyPhbUJzeaZi +e2FJwKAatbQhU3VuZSBWdW9yZWxhIDxkZWJpYW5AcHVzbGluZy5jb20+iQI3BBMB +AgAhAhsvAh4BAheABQJKrxhqBQsJCAcDBRUKCQgLBRYCAwEAAAoJEBowdl3x8NPt +wIAQAKo4Ar4JhH7GMjWGjPa6I5h6jR+IqSQWao1ptzHoJojW1VLmXXrQwe+tCLnE +AmPpSmTIDQIKnedMPwwcaC4l05/jXE/PkgtVoCaHw199/N2uMlfhdE/UtDQYj8Zj +HyTUILa4d+kle5iUKz4SNKQe0i+8howKjaNqDR5FhUj0MNZRfoyipGq5pTr8m44h +s3U6o8rZ3gP4jOuguSlNRjQbx6N7fxoTmTBYhRRGI9G59bDqI6zhmqz57FIsy3Lz +iY2T+EPaLWquwb3t3PR27vrw24HU0hHtbOy9mOTfjYJ6I+XWNB6tfATwz51fjDfV +guO4P+HwxM7GT26MuNCGWZhDQkomYZdU3UIl6agrtnBeyzJA+84j6SKyKIylFDKR +4l3PHv1S/DUIgptwurdCDiwkTXburM1KeyTJCbQx+FZcMlefQBzMxp5nrC+CY3Rq +DDlonxTuKVIS1ikHzyhZV7XzTonTYi/krjZvdgWWP3yKflpT3EsB0+4Qzwjq3h+a +aC8g8c84HoQcAqzEi1X2G52a66zam0y9uNMVx77D4fJ7QyqsdKyZTEFQUKr0lgF6 +pA0WKq6+PeXYGZVxF8ETv/2SNuyPKwJVSPwTKRK/OlUR/G8eKgqMtN6EoCwew5s8 +aeEmd+tqF7XInYTuR7dDE2vXLWWzhwGGIyhtZarEO3rf6bYruQINBEqvGIoBEADc +lewp/1llMT52Z0QlhuIJ+yf68Drp9S4LFYi66W9YJTDnmOEcKKDhsEWAWZtjhtZD +hyuzlBXgu5LhuJZTtRD6ka3K/x++R7vj/cdKoKN+TT3lsYdgLqEHhLDz6lXl9Abm +JSjpgCcpF/RYYgMwEEkGv87Vith/1XsFcV4ZriaM3tslehqMiaxyNDgkr3V5LjPs +wI8HOadCoiNnSBTpvj7oeZg8toVWDQsNju2rIBbbQxbLpi4Qbi0A9lPcwRZZ6mya +a17wBP+IAenyKvKwmcj+bU0ME2ztqeYKR4QN6izTx+0zcwRGT7isneDmH/6kIQr3 ++irN7QLlNUppfpJhbOrvn4/cqCYymUaqNPlu2BztJyYu3qxCxKy+tNKonzd/B3p7 +Yc9oA+w+T+DCAM8h/7o4gl2wtwJ4CJAezKxPoOOX1jGS4Ps0BxwMYED/L8o7SSGt +TJNp0+sVLBel6kZ6ygr1UMHWurfjF2lvmI2e+CDXiQcGwffkfTYjuH1UrmSUfPmN +A8kATBOjU/vDQkcC44qBH+BDKaNOFKim65pxuPsqD6Z8rYOOUK7CmjO7u7cRZpOO +s+625K5EYSmGGzlnfjvcOMezAnoSCE7/x8tDGVu5hqy8kWBj2jCwDdM+3CkVQ7Jp +J5yX8aRFg/c+Y8zsGQh7TkfGMj0XaDygsA1KDMVcQQARAQABiQIfBBgBAgAJBQJK +rxiKAhsMAAoJEBowdl3x8NPtshkQAIFtLBrAWGBSUBUI/vqdXpr9zHVZz9gJFR2f +Ufo769hi5qTRPjWvRsT85JV+pSfThfn5JYbRfdB5VzS44nFMM89pYQLCBGKh6N7s +aX8ArD7Uhf4m7Yk6AIcAXyCFIWG7EP0PCkEGAdGDj7/0xbJXg8m32BBOn9EmChFV +XjqC8TZ+H5lvrhnYS04owEnqLBnlBMzE+RgMmj8mpwi7tA80kQzomSuCYkGA7Phg +VmGTZDbjSVZhFnA9tLI6YHydy/VjtdA8zTrpR93TEtj5oozyBgF8aB6D2ZBWbwc3 +ZpBW3Oaf0Q/e7PWBJPmW8jemucOIrNP+n7HaDFKxpQxCBvrr5650PYZV0U7LruR3 +uZL1RPNqtXkDX+xP50rcaP1nQdOu9+BpihX8ir9vcpwFc9zxgTpafhynIFVAdx9q +QuNTzF/feKMz7WyDVKCUdhRuY0VR5MEQHkenh7JVECtTvB1Opoz3JVbqmCMdq6IS +JyIO2k69ePBNa/hhJ23LVQxT99sfb3iLeV80sSeFxb5a36J0idLQowcRmuQrzQ9R +EWFpn5h7H42EdyeWMz+Da5Nnb6fGoE2/+tBdxLkKKUBWDzyxrncU9EF6oifKKbnE +1aHNwe5oF5wSIdZIBcgNp6lfGwHiIZ0+zfmhASOF6MKLLUHAZhfUbYUWjufJF/AD +xujPUZsL +=x7r+ +-----END PGP PUBLIC KEY BLOCK----- +)ZxZ"; +#endif + +class TestSignatureBasics : public QObject +{ + Q_OBJECT +public: + explicit TestSignatureBasics(QObject *parent = nullptr) : QObject(parent) { } + + std::unique_ptr doc; + + static std::unique_ptr tmpdir; + static void initMain() + { + tmpdir = std::make_unique(); + qputenv("GNUPGHOME", tmpdir->path().toLocal8Bit().data()); + } + +private Q_SLOTS: + void init(); + void initTestCase_data(); + void initTestCase() { } + void cleanupTestCase(); + void testSignatureCount(); + void testSignatureSizes(); + void testSignerInfo(); // names and stuff + void testSignedRanges(); + void testPgp(); +}; + +std::unique_ptr TestSignatureBasics::tmpdir; + +void TestSignatureBasics::init() +{ +#ifdef ENABLE_SIGNATURES + QFETCH_GLOBAL(CryptoSign::Backend::Type, backend); + CryptoSign::Factory::setPreferredBackend(backend); + QCOMPARE(CryptoSign::Factory::getActive(), backend); +#endif + + globalParams = std::make_unique(); + doc = std::make_unique(std::make_unique(TESTDATADIR "/unittestcases/pdf-signature-sample-2sigs.pdf")); + QVERIFY(doc); + QVERIFY(doc->isOk()); +} + +void TestSignatureBasics::initTestCase_data() +{ + QTest::addColumn("backend"); + +#ifdef ENABLE_SIGNATURES + const auto availableBackends = CryptoSign::Factory::getAvailable(); + +# ifdef ENABLE_NSS3 + if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::NSS3) != availableBackends.end()) { + QTest::newRow("nss") << CryptoSign::Backend::Type::NSS3; + } else { + QWARN("Compiled with NSS3, but NSS not functional"); + } +# endif +# ifdef ENABLE_GPGME + if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::GPGME) != availableBackends.end()) { + QTest::newRow("gpg") << CryptoSign::Backend::Type::GPGME; + } else { + QWARN("Compiled with GPGME, but GPGME not functional"); + } +# endif +#endif +} + +void TestSignatureBasics::cleanupTestCase() +{ + globalParams.reset(); +} + +void TestSignatureBasics::testSignatureCount() +{ + QVERIFY(doc); + auto signatureFields = doc->getSignatureFields(); + QCOMPARE(signatureFields.size(), 4); + // count active signatures + QVERIFY(!signatureFields[0]->getSignature().empty()); + QVERIFY(!signatureFields[1]->getSignature().empty()); + QVERIFY(signatureFields[2]->getSignature().empty()); + QVERIFY(signatureFields[3]->getSignature().empty()); +} + +void TestSignatureBasics::testSignatureSizes() +{ + auto signatureFields = doc->getSignatureFields(); + // These are not the actual signature lengths, but rather + // the length of the signature field, which is likely + // a padded field. At least the pdf specification suggest to pad + // the field. + // Poppler before 23.04 did not have a padded field, later versions do. + QCOMPARE(signatureFields[0]->getSignature().size(), 10230); // Signature data size is 2340 + QCOMPARE(signatureFields[1]->getSignature().size(), 10196); // Signature data size is 2340 +} + +void TestSignatureBasics::testSignerInfo() +{ + auto signatureFields = doc->getSignatureFields(); + QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" }); + QCOMPARE(signatureFields[0]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached); + auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {}); + signatureFields[0]->validateSignatureResult(); +#ifdef ENABLE_SIGNATURES + QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" }); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256); + QCOMPARE(siginfo0->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8); +#else + QCOMPARE(siginfo0->getSignerName(), std::string {}); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Unknown); +#endif + QCOMPARE(siginfo0->getSigningTime(), time_t(1677570911)); + + QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" }); + QCOMPARE(signatureFields[1]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached); + auto siginfo1 = signatureFields[1]->validateSignatureAsync(false, false, -1 /* now */, false, false, {}); + signatureFields[1]->validateSignatureResult(); +#ifdef ENABLE_SIGNATURES + QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" }); + QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256); + QFETCH_GLOBAL(CryptoSign::Backend::Type, backend); + if (backend == CryptoSign::Backend::Type::GPGME) { + QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8); + } else if (backend == CryptoSign::Backend::Type::NSS3) { + // Not fully sure why it is zero here, but it seems to be. + QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 0); + } +#else + QCOMPARE(siginfo1->getSignerName(), std::string {}); + QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Unknown); +#endif + QCOMPARE(siginfo1->getSigningTime(), time_t(1677840601)); + + QCOMPARE(signatureFields[2]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature2_B_" }); + QCOMPARE(signatureFields[2]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field); + QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" }); + QCOMPARE(signatureFields[3]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field); +} + +void TestSignatureBasics::testSignedRanges() +{ + auto signatureFields = doc->getSignatureFields(); + + Goffset size0; + auto sig0 = signatureFields[0]->getCheckedSignature(&size0); + QVERIFY(sig0); + auto ranges0 = signatureFields[0]->getSignedRangeBounds(); + QCOMPARE(ranges0.size(), 4); + QCOMPARE(ranges0[0], 0); + QCOMPARE(ranges0[1], 24890); + QCOMPARE(ranges0[2], 45352); + QCOMPARE(ranges0[3], 58529); + QVERIFY(ranges0[3] != size0); // signature does not cover all of it + + Goffset size1; + auto sig1 = signatureFields[1]->getCheckedSignature(&size1); + QVERIFY(sig1); + auto ranges1 = signatureFields[1]->getSignedRangeBounds(); + QCOMPARE(ranges1.size(), 4); + QCOMPARE(ranges1[0], 0); + QCOMPARE(ranges1[1], 59257); + QCOMPARE(ranges1[2], 79651); + QCOMPARE(ranges1[3], 92773); + QCOMPARE(ranges1[3], size1); // signature does cover all of it +} + +void TestSignatureBasics::testPgp() +{ + std::optional usedBackend; +#ifdef ENABLE_GPGME + QFETCH_GLOBAL(CryptoSign::Backend::Type, backend); + usedBackend = backend; + if (backend == CryptoSign::Backend::Type::GPGME) { + GpgSignatureConfiguration::setPgpSignaturesAllowed(true); + + /* We need to fetch the key from the internet or elsewhere in order to get info about the key + Alternatively we could embed it in the code here*/ + auto ctx = GpgME::Context::create(GpgME::Protocol::OpenPGP); + GpgME::Data d; + d.write(pubkey, strlen(pubkey)); + d.rewind(); + auto result = ctx->importKeys(d); + QCOMPARE(result.numImported(), 1); + } +#endif + auto gpgDoc = std::make_unique(std::make_unique(TESTDATADIR "/unittestcases/some-text-pgp_signed.pdf")); + auto signatureFields = gpgDoc->getSignatureFields(); + QCOMPARE(signatureFields.size(), 1); + QCOMPARE(signatureFields[0]->getSignatureType(), CryptoSign::SignatureType::g10c_pgp_signature_detached); + QVERIFY(!signatureFields[0]->getSignature().empty()); + Goffset size0; + auto sig0 = signatureFields[0]->getCheckedSignature(&size0); + QVERIFY(sig0); + auto ranges0 = signatureFields[0]->getSignedRangeBounds(); + QCOMPARE(ranges0.size(), 4); + QCOMPARE(ranges0[0], 0); + QCOMPARE(ranges0[1], 82991); + QCOMPARE(ranges0[2], 102993); + QCOMPARE(ranges0[3], 103534); + + auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {}); + signatureFields[0]->validateSignatureResult(); + if (usedBackend == CryptoSign::Backend::Type::GPGME) { + QCOMPARE(siginfo0->getSignerName(), std::string { "Sune Vuorela" }); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256); + QCOMPARE(siginfo0->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 4096); + QCOMPARE(siginfo0->getCertificateInfo()->getNickName().toStr().substr(32), std::string { "F1F0D3ED" }); + QCOMPARE(siginfo0->getSignatureValStatus(), SignatureValidationStatus::SIGNATURE_VALID); + } else { + QCOMPARE(siginfo0->getSignerName(), std::string {}); + QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Unknown); + QCOMPARE(siginfo0->getCertificateInfo(), nullptr); + QCOMPARE(siginfo0->getSignatureValStatus(), SignatureValidationStatus::SIGNATURE_NOT_VERIFIED); + } +} + +QTEST_GUILESS_MAIN(TestSignatureBasics) +#include "check_signature_basics.moc" diff --git a/qt6/tests/check_strings.cpp b/qt6/tests/check_strings.cpp index 0fe8a7e..69cadc4 100644 --- a/qt6/tests/check_strings.cpp +++ b/qt6/tests/check_strings.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2010, 2011, Pino Toscano * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, + * Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,12 +18,13 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include +#include "UTF.h" Q_DECLARE_METATYPE(GooString *) Q_DECLARE_METATYPE(Unicode *) @@ -34,6 +36,11 @@ class TestStrings : public QObject public: explicit TestStrings(QObject *parent = nullptr) : QObject(parent) { } + GooString *newGooString(const char *s); + GooString *newGooString(const char *s, int l); + + QVector m_gooStrings; + private slots: void initTestCase(); void cleanupTestCase(); @@ -45,12 +52,6 @@ private slots: void check_QStringToUnicodeGooString(); void check_QStringToGooString_data(); void check_QStringToGooString(); - -private: - GooString *newGooString(const char *s); - GooString *newGooString(const char *s, int l); - - QVector m_gooStrings; }; void TestStrings::initTestCase() @@ -189,17 +190,15 @@ void TestStrings::check_QStringToUnicodeGooString() QFETCH(QString, string); QFETCH(QByteArray, result); - GooString *goo = Poppler::QStringToUnicodeGooString(string); + std::unique_ptr goo = Poppler::QStringToUnicodeGooString(string); if (string.isEmpty()) { QVERIFY(goo->toStr().empty()); QCOMPARE(goo->getLength(), 0); } else { - QVERIFY(goo->hasUnicodeMarker()); + QVERIFY(hasUnicodeByteOrderMark(goo->toStr())); QCOMPARE(goo->getLength(), string.length() * 2 + 2); QCOMPARE(result, QByteArray::fromRawData(goo->c_str() + 2, goo->getLength() - 2)); } - - delete goo; } void TestStrings::check_QStringToGooString_data() @@ -218,10 +217,8 @@ void TestStrings::check_QStringToGooString() QFETCH(QString, string); QFETCH(GooString *, result); - GooString *goo = Poppler::QStringToGooString(string); + const std::unique_ptr goo = Poppler::QStringToGooString(string); QCOMPARE(goo->c_str(), result->c_str()); - - delete goo; } GooString *TestStrings::newGooString(const char *s) diff --git a/qt6/tests/check_stroke_opacity.cpp b/qt6/tests/check_stroke_opacity.cpp index 9ece677..fa69ad1 100644 --- a/qt6/tests/check_stroke_opacity.cpp +++ b/qt6/tests/check_stroke_opacity.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include diff --git a/qt6/tests/check_utf8document.cpp b/qt6/tests/check_utf8document.cpp new file mode 100644 index 0000000..165e318 --- /dev/null +++ b/qt6/tests/check_utf8document.cpp @@ -0,0 +1,58 @@ +#include + +#include "PDFDoc.h" +#include "GlobalParams.h" + +#include "Outline.h" +#include "poppler-private.h" + +class TestUtf8Document : public QObject +{ + Q_OBJECT +public: + explicit TestUtf8Document(QObject *parent = nullptr) : QObject(parent) { } +private Q_SLOTS: + void checkStrings(); +}; + +inline QString outlineItemTitle(OutlineItem *item) +{ + if (!item) { + return {}; + } + const std::vector &title = item->getTitle(); + return QString::fromUcs4(reinterpret_cast(title.data()), title.size()); +} + +void TestUtf8Document::checkStrings() +{ + + globalParams = std::make_unique(); + auto doc = std::make_unique(std::make_unique(TESTDATADIR "/unittestcases/pdf20-utf8-test.pdf")); + QVERIFY(doc); + QVERIFY(doc->isOk()); + + QVERIFY(doc->getOptContentConfig() && doc->getOptContentConfig()->hasOCGs()); + + QCOMPARE(Poppler::UnicodeParsedString(doc->getDocInfoTitle().get()), QString::fromUtf8("表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀")); // clazy:exclude=qstring-allocations + + QSet expectedNames { QString::fromUtf8("گچپژ"), QString::fromUtf8("Layer 1") }; // clazy:exclude=qstring-allocations + QSet foundNames; + + for (auto &[ref, group] : doc->getOptContentConfig()->getOCGs()) { + foundNames.insert(Poppler::UnicodeParsedString(group->getName())); + } + QCOMPARE(expectedNames, foundNames); + + auto outlineItems = doc->getOutline()->getItems(); + QVERIFY(outlineItems); + QCOMPARE(outlineItems->size(), 3); + + QCOMPARE(outlineItemTitle(outlineItems->at(0)), QString::fromUtf8("PDF 2.0 with UTF-8 test file")); // clazy:exclude=qstring-allocations + QCOMPARE(outlineItemTitle(outlineItems->at(1)), QString::fromUtf8("\u202A\u202Atest\u202A")); // clazy:exclude=qstring-allocations + QCOMPARE(outlineItemTitle(outlineItems->at(2)), QString::fromUtf8("🌈️\n" /*emoji rainbow flag*/)); // clazy:exclude=qstring-allocations +} + +QTEST_GUILESS_MAIN(TestUtf8Document) + +#include "check_utf8document.moc" diff --git a/qt6/tests/check_utf_conversion.cpp b/qt6/tests/check_utf_conversion.cpp index 2cac758..831f002 100644 --- a/qt6/tests/check_utf_conversion.cpp +++ b/qt6/tests/check_utf_conversion.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -21,21 +21,6 @@ private slots: void testUnicodeLittleEndian(); }; -static bool compare(const char *a, const char *b) -{ - return strcmp(a, b) == 0; -} - -static bool compare(const uint16_t *a, const uint16_t *b) -{ - while (*a && *b) { - if (*a++ != *b++) { - return false; - } - } - return *a == *b; -} - static bool compare(const Unicode *a, const char *b, int len) { for (int i = 0; i < len; i++) { @@ -77,49 +62,36 @@ void TestUTFConversion::testUTF_data() void TestUTFConversion::testUTF() { - char utf8Buf[1000]; - char *utf8String; - uint16_t utf16Buf[1000]; - uint16_t *utf16String; + std::string utf8String; int len; QFETCH(QString, s); - char *str = strdup(s.toUtf8().constData()); + const std::string str = s.toStdString(); // UTF-8 to UTF-16 len = utf8CountUtf16CodeUnits(str); QCOMPARE(len, s.size()); // QString size() returns number of code units, not code points - Q_ASSERT(len < (int)sizeof(utf16Buf)); // if this fails, make utf16Buf larger - len = utf8ToUtf16(str, utf16Buf); - QVERIFY(compare(utf16Buf, s.utf16())); + std::u16string utf16String = utf8ToUtf16(str); + QCOMPARE(utf16String, s.toStdU16String()); QCOMPARE(len, s.size()); - utf16String = utf8ToUtf16(str); - QVERIFY(compare(utf16String, s.utf16())); - free(utf16String); - - std::string sUtf8(str); - std::unique_ptr gsUtf16_a(utf8ToUtf16WithBom(sUtf8)); + std::string gsUtf16_a(utf8ToUtf16WithBom(str)); std::unique_ptr gsUtf16_b(Poppler::QStringToUnicodeGooString(s)); - QCOMPARE(gsUtf16_a->cmp(gsUtf16_b.get()), 0); + QCOMPARE(gsUtf16_b->cmp(gsUtf16_a), 0); // UTF-16 to UTF-8 len = utf16CountUtf8Bytes(s.utf16()); - QCOMPARE(len, (int)strlen(str)); - Q_ASSERT(len < (int)sizeof(utf8Buf)); // if this fails, make utf8Buf larger + QCOMPARE(len, str.size()); - len = utf16ToUtf8(s.utf16(), utf8Buf); - QVERIFY(compare(utf8Buf, str)); - QCOMPARE(len, (int)strlen(str)); + utf8String = utf16ToUtf8(s.utf16(), INT_MAX); + QCOMPARE(utf8String, str); + QCOMPARE(len, str.size()); utf8String = utf16ToUtf8(s.utf16()); - QVERIFY(compare(utf8String, str)); - free(utf8String); - - free(str); + QCOMPARE(utf8String, str); } void TestUTFConversion::testUnicodeToAscii7() @@ -129,24 +101,19 @@ void TestUTFConversion::testUnicodeToAscii7() // Test string is one 'Registered' and twenty 'Copyright' chars // so it's long enough to reproduce the bug given that glibc // malloc() always returns 8-byte aligned memory addresses. - GooString *goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations - - Unicode *in; - const int in_len = TextStringToUCS4(goo->toStr(), &in); + std::unique_ptr goo = Poppler::QStringToUnicodeGooString(QString::fromUtf8("®©©©©©©©©©©©©©©©©©©©©")); // clazy:exclude=qstring-allocations - delete goo; + const std::vector in = TextStringToUCS4(goo->toStr()); int in_norm_len; int *in_norm_idx; - Unicode *in_norm = unicodeNormalizeNFKC(in, in_len, &in_norm_len, &in_norm_idx, true); - - free(in); + Unicode *in_norm = unicodeNormalizeNFKC(in.data(), in.size(), &in_norm_len, &in_norm_idx, true); Unicode *out; int out_len; int *out_ascii_idx; - unicodeToAscii7(in_norm, in_norm_len, &out, &out_len, in_norm_idx, &out_ascii_idx); + unicodeToAscii7(std::span(in_norm, in_norm_len), &out, &out_len, in_norm_idx, &out_ascii_idx); free(in_norm); free(in_norm_idx); @@ -172,25 +139,24 @@ void TestUTFConversion::testUnicodeLittleEndian() // Let's assert both GooString's are different QVERIFY(GooUTF16LE != GooUTF16BE); - Unicode *UCS4fromLE, *UCS4fromBE; - const int len1 = TextStringToUCS4(GooUTF16LE, &UCS4fromLE); - const int len2 = TextStringToUCS4(GooUTF16BE, &UCS4fromBE); + const std::vector UCS4fromLE = TextStringToUCS4(GooUTF16LE); + const std::vector UCS4fromBE = TextStringToUCS4(GooUTF16BE); // len is 4 because TextStringToUCS4() removes the two leading Byte Order Mark (BOM) code points - QCOMPARE(len1, len2); - QCOMPARE(len1, 4); + QCOMPARE(UCS4fromLE.size(), UCS4fromBE.size()); + QCOMPARE(UCS4fromLE.size(), 4); // Check that now after conversion, UCS4fromLE and UCS4fromBE are now the same - for (int i = 0; i < len1; i++) { + for (size_t i = 0; i < UCS4fromLE.size(); i++) { QCOMPARE(UCS4fromLE[i], UCS4fromBE[i]); } const QString expected = QStringLiteral("HI!☑"); // Do some final verifications, checking the strings to be "HI!" - QVERIFY(*UCS4fromLE == *UCS4fromBE); - QVERIFY(compare(UCS4fromLE, expected.utf16(), len1)); - QVERIFY(compare(UCS4fromBE, expected.utf16(), len1)); + QVERIFY(UCS4fromLE == UCS4fromBE); + QVERIFY(compare(UCS4fromLE.data(), expected.utf16(), UCS4fromLE.size())); + QVERIFY(compare(UCS4fromBE.data(), expected.utf16(), UCS4fromBE.size())); } QTEST_GUILESS_MAIN(TestUTFConversion) diff --git a/qt6/tests/poppler-forms.cpp b/qt6/tests/poppler-forms.cpp index d52bab4..5961c26 100644 --- a/qt6/tests/poppler-forms.cpp +++ b/qt6/tests/poppler-forms.cpp @@ -123,6 +123,8 @@ static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationI case Poppler::SignatureValidationInfo::CertificateNotVerified: out << "NotVerifiedYet"; break; + case Poppler::SignatureValidationInfo::CertificateVerificationInProgress: + out << "InProgress"; } return out; } @@ -252,9 +254,10 @@ int main(int argc, char **argv) case Poppler::FormField::FormSignature: { const Poppler::FormFieldSignature *signatureForm = static_cast(form.get()); - const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate); + const Poppler::SignatureValidationInfo svi = signatureForm->validateAsync(Poppler::FormFieldSignature::ValidateVerifyCertificate).first; + const Poppler::SignatureValidationInfo::CertificateStatus certStatus = signatureForm->validateResult(); std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl; - std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl; + std::cout << "\t\t\tCertificateStatus: " << certStatus << std::endl; if (svi.signerName().isEmpty() == false) { std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl; } else { diff --git a/qt6/tests/stress-poppler-dir.cpp b/qt6/tests/stress-poppler-dir.cpp index 3f05444..16ff711 100644 --- a/qt6/tests/stress-poppler-dir.cpp +++ b/qt6/tests/stress-poppler-dir.cpp @@ -19,7 +19,7 @@ int main(int argc, char **argv) foreach (const QString &fileName, directory.entryList()) { if (fileName.endsWith(QStringLiteral("pdf"))) { qDebug() << "Doing" << fileName.toLatin1().data() << ":"; - std::unique_ptr doc = Poppler::Document::load(directory.canonicalPath() + "/" + fileName); + std::unique_ptr doc = Poppler::Document::load(directory.canonicalPath() + '/' + fileName); if (!doc) { qWarning() << "doc not loaded"; } else if (doc->isLocked()) { diff --git a/qt6/tests/stress-poppler-qt6.cpp b/qt6/tests/stress-poppler-qt6.cpp index f613b82..ac91f14 100644 --- a/qt6/tests/stress-poppler-qt6.cpp +++ b/qt6/tests/stress-poppler-qt6.cpp @@ -30,7 +30,7 @@ int main(int argc, char **argv) if (excludeSubDirs.contains(subdir)) { // then skip it } else { - QString path = "./pdfdb/" + subdir + "/data.pdf"; + QString path = QStringLiteral("./pdfdb/") + subdir + QStringLiteral("/data.pdf"); std::cout << "Doing " << path.toLatin1().data() << " :"; std::unique_ptr doc = Poppler::Document::load(path); if (!doc) { diff --git a/qt6/tests/stress-threads-qt6.cpp b/qt6/tests/stress-threads-qt6.cpp index 1188765..f12a85e 100644 --- a/qt6/tests/stress-threads-qt6.cpp +++ b/qt6/tests/stress-threads-qt6.cpp @@ -61,7 +61,7 @@ static std::unique_ptr loadRandomPage(Poppler::Document *document return loadPage(document, QRandomGenerator::global()->bounded(document->numPages())); } -SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document), m_pages() +SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document) { m_pages.reserve(m_document->numPages()); diff --git a/qt6/tests/test-password-qt6.cpp b/qt6/tests/test-password-qt6.cpp index 339e75f..55b76c1 100644 --- a/qt6/tests/test-password-qt6.cpp +++ b/qt6/tests/test-password-qt6.cpp @@ -46,7 +46,7 @@ void PDFDisplay::display() } } -PDFDisplay::~PDFDisplay() { } +PDFDisplay::~PDFDisplay() = default; void PDFDisplay::paintEvent(QPaintEvent *e) { @@ -121,7 +121,7 @@ int main(int argc, char **argv) test.setWindowTitle(QStringLiteral("Poppler-Qt6 Test")); test.show(); // show it - return a.exec(); // start event loop + return QApplication::exec(); // start event loop } #include "test-password-qt6.moc" diff --git a/qt6/tests/test-poppler-qt6.cpp b/qt6/tests/test-poppler-qt6.cpp index c69a987..30744c3 100644 --- a/qt6/tests/test-poppler-qt6.cpp +++ b/qt6/tests/test-poppler-qt6.cpp @@ -81,7 +81,7 @@ void PDFDisplay::display() } } -PDFDisplay::~PDFDisplay() { } +PDFDisplay::~PDFDisplay() = default; void PDFDisplay::paintEvent(QPaintEvent *e) { @@ -195,13 +195,13 @@ int main(int argc, char **argv) test.display(); test.show(); // show it - return a.exec(); // start event loop + return QApplication::exec(); // start event loop } else { std::unique_ptr page = doc->page(0); QLabel *l = new QLabel(page->text(QRectF()), nullptr); l->show(); - return a.exec(); + return QApplication::exec(); } } diff --git a/splash/Splash.cc b/splash/Splash.cc index 05f000d..3d2576a 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -11,7 +11,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005-2022 Albert Astals Cid +// Copyright (C) 2005-2024 Albert Astals Cid // Copyright (C) 2005 Marco Pesenti Gritti // Copyright (C) 2010-2016 Thomas Freitag // Copyright (C) 2010 Christian Feuersänger @@ -24,7 +24,7 @@ // Copyright (C) 2019, 2020 Oliver Sander // Copyright (C) 2019 Marek Kasik // Copyright (C) 2020 Tobias Deiminger -// Copyright (C) 2021 Even Rouault +// Copyright (C) 2021, 2024 Even Rouault // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -196,7 +196,7 @@ SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResu //------------------------------------------------------------------------ -static void blendXor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) +static void blendXor(SplashColorPtr src, SplashColorPtr dest, SplashColorPtr blend, SplashColorMode cm) // NOLINT(readability-non-const-parameter) Needs to be of type SplashBlendFunc { int i; @@ -1889,7 +1889,7 @@ SplashError Splash::stroke(SplashPath *path) SplashCoord d1, d2, t1, t2, w; if (debugMode) { - printf("stroke [dash:%ld] [width:%.2f]:\n", state->lineDash.size(), (double)state->lineWidth); + printf("stroke [dash:%zu] [width:%.2f]:\n", state->lineDash.size(), (double)state->lineWidth); dumpPath(path); } opClipRes = splashClipAllOutside; @@ -3126,10 +3126,15 @@ void Splash::scaleMaskYdownXdown(SplashImageMaskSource src, void *srcData, int s xq = srcWidth % scaledWidth; // allocate buffers - lineBuf = (unsigned char *)gmalloc(srcWidth); + lineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf in Splash::scaleMaskYdownXdown"); + return; + } + pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); if (unlikely(!pixBuf)) { - error(errInternal, -1, "Couldn't allocate memory for pixBux in Splash::scaleMaskYdownXdown"); + error(errInternal, -1, "Couldn't allocate memory for pixBuf in Splash::scaleMaskYdownXdown"); gfree(lineBuf); return; } @@ -3216,8 +3221,18 @@ void Splash::scaleMaskYdownXup(SplashImageMaskSource src, void *srcData, int src xq = scaledWidth % srcWidth; // allocate buffers - lineBuf = (unsigned char *)gmalloc(srcWidth); - pixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); + lineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf in Splash::scaleMaskYdownXup"); + return; + } + + pixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); + if (unlikely(!pixBuf)) { + error(errInternal, -1, "Couldn't allocate memory for pixBuf in Splash::scaleMaskYdownXup"); + gfree(lineBuf); + return; + } // init y scale Bresenham yt = 0; @@ -3294,7 +3309,11 @@ void Splash::scaleMaskYupXdown(SplashImageMaskSource src, void *srcData, int src xq = srcWidth % scaledWidth; // allocate buffers - lineBuf = (unsigned char *)gmalloc(srcWidth); + lineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf in Splash::scaleMaskYupXdown"); + return; + } // init y scale Bresenham yt = 0; @@ -3380,7 +3399,11 @@ void Splash::scaleMaskYupXup(SplashImageMaskSource src, void *srcData, int srcWi xq = scaledWidth % srcWidth; // allocate buffers - lineBuf = (unsigned char *)gmalloc(srcWidth); + lineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf in Splash::scaleMaskYupXup"); + return; + } // init y scale Bresenham yt = 0; @@ -3682,11 +3705,19 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTran // compute the scale factors if (splashAbs(mat[0]) >= splashAbs(mat[1])) { - scaledWidth = xMax - xMin; - scaledHeight = yMax - yMin; + if (unlikely(checkedSubtraction(xMax, xMin, &scaledWidth))) { + return splashErrBadArg; + } + if (unlikely(checkedSubtraction(yMax, yMin, &scaledHeight))) { + return splashErrBadArg; + } } else { - scaledWidth = yMax - yMin; - scaledHeight = xMax - xMin; + if (unlikely(checkedSubtraction(yMax, yMin, &scaledWidth))) { + return splashErrBadArg; + } + if (unlikely(checkedSubtraction(xMax, xMin, &scaledHeight))) { + return splashErrBadArg; + } } if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) { if (mat[0] >= 0) { @@ -3892,6 +3923,9 @@ SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTran if (xa == xb) { ++xb; } + if (unlikely(clipRes == splashClipAllInside && xb > bitmap->getWidth())) { + xb = bitmap->getWidth(); + } if (clipRes != splashClipAllInside) { clipRes2 = state->clip->testSpan(xa, xb - 1, y); } else { @@ -4015,8 +4049,21 @@ bool Splash::scaleImageYdownXdown(SplashImageSource src, void *srcData, SplashCo return false; } if (srcAlpha) { - alphaLineBuf = (unsigned char *)gmalloc(srcWidth); - alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); + alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!alphaLineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYdownXdown"); + gfree(lineBuf); + gfree(pixBuf); + return false; + } + alphaPixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); + if (unlikely(!alphaPixBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaPixBuf in Splash::scaleImageYdownXdown"); + gfree(lineBuf); + gfree(pixBuf); + gfree(alphaLineBuf); + return false; + } } else { alphaLineBuf = nullptr; alphaPixBuf = nullptr; @@ -4248,10 +4295,28 @@ bool Splash::scaleImageYdownXup(SplashImageSource src, void *srcData, SplashColo error(errInternal, -1, "Splash::scaleImageYdownXup. Couldn't allocate pixBuf memory"); return false; } - lineBuf = (unsigned char *)gmallocn(srcWidth, nComps); + lineBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth, nComps); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Splash::scaleImageYdownXup. Couldn't allocate lineBuf memory"); + gfree(pixBuf); + return false; + } if (srcAlpha) { - alphaLineBuf = (unsigned char *)gmalloc(srcWidth); - alphaPixBuf = (unsigned int *)gmallocn(srcWidth, sizeof(int)); + alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!alphaLineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYdownXup"); + gfree(lineBuf); + gfree(pixBuf); + return false; + } + alphaPixBuf = (unsigned int *)gmallocn_checkoverflow(srcWidth, sizeof(int)); + if (unlikely(!alphaPixBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaPixBuf in Splash::scaleImageYdownXup"); + gfree(lineBuf); + gfree(pixBuf); + gfree(alphaLineBuf); + return false; + } } else { alphaLineBuf = nullptr; alphaPixBuf = nullptr; @@ -4400,7 +4465,12 @@ bool Splash::scaleImageYupXdown(SplashImageSource src, void *srcData, SplashColo return false; } if (srcAlpha) { - alphaLineBuf = (unsigned char *)gmalloc(srcWidth); + alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!alphaLineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYupXdown"); + gfree(lineBuf); + return false; + } } else { alphaLineBuf = nullptr; } @@ -4555,8 +4625,18 @@ bool Splash::scaleImageYupXup(SplashImageSource src, void *srcData, SplashColorM // allocate buffers lineBuf = (unsigned char *)gmallocn(srcWidth, nComps); + if (unlikely(!lineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf in Splash::scaleImageYupXup"); + return false; + } + if (srcAlpha) { - alphaLineBuf = (unsigned char *)gmalloc(srcWidth); + alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth); + if (unlikely(!alphaLineBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYupXup"); + gfree(lineBuf); + return false; + } } else { alphaLineBuf = nullptr; } @@ -4728,13 +4808,57 @@ bool Splash::scaleImageYupXupBilinear(SplashImageSource src, void *srcData, Spla } // allocate buffers - srcBuf = (unsigned char *)gmallocn(srcWidth + 1, nComps); // + 1 pixel of padding - lineBuf1 = (unsigned char *)gmallocn(scaledWidth, nComps); - lineBuf2 = (unsigned char *)gmallocn(scaledWidth, nComps); + srcBuf = (unsigned char *)gmallocn_checkoverflow(srcWidth + 1, nComps); // + 1 pixel of padding + if (unlikely(!srcBuf)) { + error(errInternal, -1, "Couldn't allocate memory for srcBuf in Splash::scaleImageYupXupBilinear"); + return false; + } + + lineBuf1 = (unsigned char *)gmallocn_checkoverflow(scaledWidth, nComps); + if (unlikely(!lineBuf1)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf1 in Splash::scaleImageYupXupBilinear"); + gfree(srcBuf); + return false; + } + + lineBuf2 = (unsigned char *)gmallocn_checkoverflow(scaledWidth, nComps); + if (unlikely(!lineBuf2)) { + error(errInternal, -1, "Couldn't allocate memory for lineBuf2 in Splash::scaleImageYupXupBilinear"); + gfree(srcBuf); + gfree(lineBuf1); + return false; + } + if (srcAlpha) { - alphaSrcBuf = (unsigned char *)gmalloc(srcWidth + 1); // + 1 pixel of padding - alphaLineBuf1 = (unsigned char *)gmalloc(scaledWidth); - alphaLineBuf2 = (unsigned char *)gmalloc(scaledWidth); + alphaSrcBuf = (unsigned char *)gmalloc_checkoverflow(srcWidth + 1); // + 1 pixel of padding + if (unlikely(!alphaSrcBuf)) { + error(errInternal, -1, "Couldn't allocate memory for alphaSrcBuf in Splash::scaleImageYupXupBilinear"); + gfree(srcBuf); + gfree(lineBuf1); + gfree(lineBuf2); + return false; + } + + alphaLineBuf1 = (unsigned char *)gmalloc_checkoverflow(scaledWidth); + if (unlikely(!alphaLineBuf1)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf1 in Splash::scaleImageYupXupBilinear"); + gfree(srcBuf); + gfree(lineBuf1); + gfree(lineBuf2); + gfree(alphaSrcBuf); + return false; + } + + alphaLineBuf2 = (unsigned char *)gmalloc_checkoverflow(scaledWidth); + if (unlikely(!alphaLineBuf2)) { + error(errInternal, -1, "Couldn't allocate memory for alphaLineBuf2 in Splash::scaleImageYupXupBilinear"); + gfree(srcBuf); + gfree(lineBuf1); + gfree(lineBuf2); + gfree(alphaSrcBuf); + gfree(alphaLineBuf1); + return false; + } } else { alphaSrcBuf = nullptr; alphaLineBuf1 = nullptr; @@ -5033,7 +5157,7 @@ SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, if (src->getSeparationList()->size() > bitmap->getSeparationList()->size()) { for (x = bitmap->getSeparationList()->size(); x < (int)src->getSeparationList()->size(); x++) { - bitmap->getSeparationList()->push_back((GfxSeparationColorSpace *)((*src->getSeparationList())[x])->copy()); + bitmap->getSeparationList()->push_back(((*src->getSeparationList())[x])->copyAsOwnType()); } } if (src->alpha) { @@ -5366,8 +5490,36 @@ bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading) // this here is det( T ) == 0 // where T is the matrix to map to barycentric coordinates. - if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0) { - continue; // degenerate triangle. + { + int x02diff; + if (checkedSubtraction(x[0], x[2], &x02diff)) { + continue; + } + int y12diff; + if (checkedSubtraction(y[1], y[2], &y12diff)) { + continue; + } + int x12diff; + if (checkedSubtraction(x[1], x[2], &x12diff)) { + continue; + } + int y02diff; + if (checkedSubtraction(y[0], y[2], &y02diff)) { + continue; + } + + int x02diffY12diff; + if (checkedMultiply(x02diff, y12diff, &x02diffY12diff)) { + continue; + } + int x12diffY02diff; + if (checkedMultiply(x12diff, y02diff, &x12diffY02diff)) { + continue; + } + + if (x02diffY12diff - x12diffY02diff == 0) { + continue; // degenerate triangle. + } } // this here initialises the scanline generation. @@ -5770,7 +5922,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int x case splashModeMono8: for (y = 0; y < height; ++y) { p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest]; - sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc]; + sp = &src->data[(ySrc + y) * src->rowSize + xSrc]; for (x = 0; x < width; ++x) { *p++ = *sp++; } diff --git a/splash/Splash.h b/splash/Splash.h index 6616751..e13be54 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -242,18 +242,9 @@ class POPPLER_PRIVATE_EXPORT Splash void setDebugMode(bool debugModeA) { debugMode = debugModeA; } #if 1 //~tmp: turn off anti-aliasing temporarily - void setInShading(bool sh) - { - inShading = sh; - } - bool getVectorAntialias() - { - return vectorAntialias; - } - void setVectorAntialias(bool vaa) - { - vectorAntialias = vaa; - } + void setInShading(bool sh) { inShading = sh; } + bool getVectorAntialias() { return vectorAntialias; } + void setVectorAntialias(bool vaa) { vectorAntialias = vaa; } #endif // Do shaded fills with dynamic patterns diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index af4a622..c204a62 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -11,7 +11,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2006, 2009, 2010, 2012, 2015, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2006, 2009, 2010, 2012, 2015, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2007 Ilmari Heikkinen // Copyright (C) 2009 Shen Liang // Copyright (C) 2009 Stefan Thomas @@ -53,7 +53,7 @@ // SplashBitmap //------------------------------------------------------------------------ -SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode modeA, bool alphaA, bool topDown, const std::vector *separationListA) +SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode modeA, bool alphaA, bool topDown, const std::vector> *separationListA) { width = widthA; height = heightA; @@ -122,10 +122,10 @@ SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPadA, SplashColorMode } else { alpha = nullptr; } - separationList = new std::vector(); + separationList = new std::vector>(); if (separationListA != nullptr) { - for (const GfxSeparationColorSpace *separation : *separationListA) { - separationList->push_back((GfxSeparationColorSpace *)separation->copy()); + for (const std::unique_ptr &separation : *separationListA) { + separationList->push_back(separation->copyAsOwnType()); } } } @@ -160,9 +160,6 @@ SplashBitmap::~SplashBitmap() } } gfree(alpha); - for (auto entry : *separationList) { - delete entry; - } delete separationList; } @@ -447,13 +444,13 @@ void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) m = byteToDbl(col[1]); y = byteToDbl(col[2]); k = byteToDbl(col[3]); - if (separationList->size() > 0) { + if (!separationList->empty()) { for (std::size_t i = 0; i < separationList->size(); i++) { if (col[i + 4] > 0) { GfxCMYK cmyk; GfxColor input; input.c[0] = byteToCol(col[i + 4]); - GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); + const std::unique_ptr &sepCS = (*separationList)[i]; sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); @@ -500,13 +497,13 @@ void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, ConversionMode conve m = byteToDbl(col[1]); y = byteToDbl(col[2]); k = byteToDbl(col[3]); - if (separationList->size() > 0) { + if (!separationList->empty()) { for (std::size_t i = 0; i < separationList->size(); i++) { if (col[i + 4] > 0) { GfxCMYK cmyk; GfxColor input; input.c[0] = byteToCol(col[i + 4]); - GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); + const std::unique_ptr &sepCS = (*separationList)[i]; sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); @@ -618,7 +615,7 @@ void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) for (int x = 0; x < width; x++) { getPixel(x, yl, col); - if (separationList->size() > 0) { + if (!separationList->empty()) { double c, m, y, k; c = byteToDbl(col[0]); m = byteToDbl(col[1]); @@ -629,7 +626,7 @@ void SplashBitmap::getCMYKLine(int yl, SplashColorPtr line) GfxCMYK cmyk; GfxColor input; input.c[0] = byteToCol(col[i + 4]); - GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)((*separationList)[i]); + const std::unique_ptr &sepCS = (*separationList)[i]; sepCS->getCMYK(&input, &cmyk); col[0] = colToByte(cmyk.c); col[1] = colToByte(cmyk.m); diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 2d7cf41..367d9fe 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -13,7 +13,7 @@ // // Copyright (C) 2007 Ilmari Heikkinen // Copyright (C) 2009 Shen Liang -// Copyright (C) 2009, 2012, 2018, 2021, 2022 Albert Astals Cid +// Copyright (C) 2009, 2012, 2018, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2010, 2017 Adrian Johnson // Copyright (C) 2010 Harry Roberts @@ -36,6 +36,7 @@ #include "SplashTypes.h" #include "poppler_private_export.h" #include +#include #include #include @@ -53,7 +54,7 @@ class POPPLER_PRIVATE_EXPORT SplashBitmap // color mode . Rows will be padded out to a multiple of // bytes. If is false, the bitmap will be stored // upside-down, i.e., with the last row first in memory. - SplashBitmap(int widthA, int heightA, int rowPad, SplashColorMode modeA, bool alphaA, bool topDown = true, const std::vector *separationList = nullptr); + SplashBitmap(int widthA, int heightA, int rowPad, SplashColorMode modeA, bool alphaA, bool topDown = true, const std::vector> *separationList = nullptr); static SplashBitmap *copy(const SplashBitmap *src); ~SplashBitmap(); @@ -69,10 +70,10 @@ class POPPLER_PRIVATE_EXPORT SplashBitmap SplashColorMode getMode() const { return mode; } SplashColorPtr getDataPtr() { return data; } unsigned char *getAlphaPtr() { return alpha; } - std::vector *getSeparationList() { return separationList; } + std::vector> *getSeparationList() { return separationList; } SplashColorConstPtr getDataPtr() const { return data; } const unsigned char *getAlphaPtr() const { return alpha; } - const std::vector *getSeparationList() const { return separationList; } + const std::vector> *getSeparationList() const { return separationList; } SplashError writePNMFile(char *fileName); SplashError writePNMFile(FILE *f); @@ -119,7 +120,7 @@ class POPPLER_PRIVATE_EXPORT SplashBitmap SplashColorPtr data; // pointer to row zero of the color data unsigned char *alpha; // pointer to row zero of the alpha data // (always top-down) - std::vector *separationList; // list of spot colorants and their mapping functions + std::vector> *separationList; // list of spot colorants and their mapping functions friend class Splash; diff --git a/splash/SplashFTFont.cc b/splash/SplashFTFont.cc index a366f71..0e43a67 100644 --- a/splash/SplashFTFont.cc +++ b/splash/SplashFTFont.cc @@ -19,6 +19,7 @@ // Copyright (C) 2012 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Oliver Sander +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -147,7 +148,7 @@ SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, const isOk = true; } -SplashFTFont::~SplashFTFont() { } +SplashFTFont::~SplashFTFont() = default; bool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) { @@ -206,7 +207,7 @@ bool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bit FT_Set_Transform(ff->face, &matrix, &offset); slot = ff->face->glyph; - if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { + if (c < int(ff->codeToGID.size()) && c >= 0) { gid = (FT_UInt)ff->codeToGID[c]; } else { gid = (FT_UInt)c; @@ -285,7 +286,7 @@ double SplashFTFont::getGlyphAdvance(int c) ff->face->size = sizeObj; FT_Set_Transform(ff->face, &identityMatrix, &offset); - if (ff->codeToGID && c < ff->codeToGIDLen) { + if (c < int(ff->codeToGID.size())) { gid = (FT_UInt)ff->codeToGID[c]; } else { gid = (FT_UInt)c; @@ -310,10 +311,10 @@ SplashPath *SplashFTFont::getGlyphPath(int c) { static const FT_Outline_Funcs outlineFuncs = { #if FREETYPE_MINOR <= 1 - (int (*)(FT_Vector *, void *)) & glyphPathMoveTo, - (int (*)(FT_Vector *, void *)) & glyphPathLineTo, - (int (*)(FT_Vector *, FT_Vector *, void *)) & glyphPathConicTo, - (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *)) & glyphPathCubicTo, + (int (*)(FT_Vector *, void *))&glyphPathMoveTo, + (int (*)(FT_Vector *, void *))&glyphPathLineTo, + (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo, + (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo, #else &glyphPathMoveTo, &glyphPathLineTo, @@ -337,7 +338,7 @@ SplashPath *SplashFTFont::getGlyphPath(int c) ff->face->size = sizeObj; FT_Set_Transform(ff->face, &textMatrix, nullptr); slot = ff->face->glyph; - if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { + if (c < int(ff->codeToGID.size()) && c >= 0) { gid = ff->codeToGID[c]; } else { gid = (FT_UInt)c; diff --git a/splash/SplashFTFontEngine.cc b/splash/SplashFTFontEngine.cc index eceb969..432a9e8 100644 --- a/splash/SplashFTFontEngine.cc +++ b/splash/SplashFTFontEngine.cc @@ -12,11 +12,12 @@ // under GPL version 2 or later // // Copyright (C) 2006 Takashi Iwai -// Copyright (C) 2009, 2011, 2012, 2022 Albert Astals Cid +// Copyright (C) 2009, 2011, 2012, 2022, 2024 Albert Astals Cid // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2019 Christian Persch +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -35,6 +36,7 @@ #include "fofi/FoFiTrueType.h" #include "fofi/FoFiType1C.h" #include "SplashFTFontFile.h" +#include "SplashFontFileID.h" #include "SplashFTFontEngine.h" //------------------------------------------------------------------------ @@ -64,34 +66,34 @@ SplashFTFontEngine::~SplashFTFontEngine() FT_Done_FreeType(lib); } -SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFTFontEngine::loadType1Font(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { - return SplashFTFontFile::loadType1Font(this, idA, src, enc); + return SplashFTFontFile::loadType1Font(this, std::move(idA), src, enc, faceIndex); } -SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFTFontEngine::loadType1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { - return SplashFTFontFile::loadType1Font(this, idA, src, enc); + return SplashFTFontFile::loadType1Font(this, std::move(idA), src, enc, faceIndex); } -SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { - return SplashFTFontFile::loadType1Font(this, idA, src, enc); + return SplashFTFontFile::loadType1Font(this, std::move(idA), src, enc, faceIndex); } -SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) +SplashFontFile *SplashFTFontEngine::loadCIDFont(std::unique_ptr idA, SplashFontSrc *src, int faceIndex) { - return SplashFTFontFile::loadCIDFont(this, idA, src, nullptr, 0); + return SplashFTFontFile::loadCIDFont(this, std::move(idA), src, {}, faceIndex); } -SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen) +SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex) { - return SplashFTFontFile::loadCIDFont(this, idA, src, codeToGID ? codeToGID : nullptr, codeToGID ? codeToGIDLen : 0); + return SplashFTFontFile::loadCIDFont(this, std::move(idA), src, std::move(codeToGID), faceIndex); } -SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex) +SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex) { SplashFontFile *ret; - ret = SplashFTFontFile::loadTrueTypeFont(this, idA, src, codeToGID, codeToGIDLen, faceIndex); + ret = SplashFTFontFile::loadTrueTypeFont(this, std::move(idA), src, std::move(codeToGID), faceIndex); return ret; } diff --git a/splash/SplashFTFontEngine.h b/splash/SplashFTFontEngine.h index 607b2a6..c6df400 100644 --- a/splash/SplashFTFontEngine.h +++ b/splash/SplashFTFontEngine.h @@ -13,10 +13,11 @@ // // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2009 Petr Gajdos -// Copyright (C) 2009, 2018, 2022 Albert Astals Cid +// Copyright (C) 2009, 2018, 2022, 2024 Albert Astals Cid // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -28,6 +29,8 @@ #include #include FT_FREETYPE_H +#include +#include class SplashFontFile; class SplashFontFileID; @@ -48,12 +51,12 @@ class SplashFTFontEngine SplashFTFontEngine &operator=(const SplashFTFontEngine &) = delete; // Load fonts. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen); - SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex = 0); + SplashFontFile *loadType1Font(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadType1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadOpenTypeT1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadCIDFont(std::unique_ptr idA, SplashFontSrc *src, int faceIndex); + SplashFontFile *loadOpenTypeCFFFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex); + SplashFontFile *loadTrueTypeFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex); bool getAA() { return aa; } void setAA(bool aaA) { aa = aaA; } diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc index 216b54e..96f19f8 100644 --- a/splash/SplashFTFontFile.cc +++ b/splash/SplashFTFontFile.cc @@ -14,7 +14,8 @@ // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2014, 2017, 2022 Adrian Johnson // Copyright (C) 2017, 2018, 2022 Oliver Sander -// Copyright (C) 2018 Albert Astals Cid +// Copyright (C) 2018, 2024 Albert Astals Cid +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -30,30 +31,30 @@ #include "SplashFTFontEngine.h" #include "SplashFTFont.h" #include "SplashFTFontFile.h" +#include "SplashFontFileID.h" //------------------------------------------------------------------------ // SplashFTFontFile //------------------------------------------------------------------------ -SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, const char **encA) +SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, const char **encA, int faceIndexA) { FT_Face faceA; - int *codeToGIDA; const char *name; int i; if (src->isFile) { - if (ft_new_face_from_file(engineA->lib, src->fileName.c_str(), 0, &faceA)) { + if (ft_new_face_from_file(engineA->lib, src->fileName.c_str(), faceIndexA, &faceA)) { return nullptr; } } else { - if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), 0, &faceA)) { + if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), faceIndexA, &faceA)) { return nullptr; } } - codeToGIDA = (int *)gmallocn(256, sizeof(int)); + std::vector codeToGIDA; + codeToGIDA.resize(256, 0); for (i = 0; i < 256; ++i) { - codeToGIDA[i] = 0; if ((name = encA[i])) { codeToGIDA[i] = (int)FT_Get_Name_Index(faceA, (char *)name); if (codeToGIDA[i] == 0) { @@ -65,27 +66,27 @@ SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA, Spl } } - return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, 256, false, true); + return new SplashFTFontFile(engineA, std::move(idA), src, faceA, std::move(codeToGIDA), false, true); } -SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA) +SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGIDA, int faceIndexA) { FT_Face faceA; if (src->isFile) { - if (ft_new_face_from_file(engineA->lib, src->fileName.c_str(), 0, &faceA)) { + if (ft_new_face_from_file(engineA->lib, src->fileName.c_str(), faceIndexA, &faceA)) { return nullptr; } } else { - if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), 0, &faceA)) { + if (FT_New_Memory_Face(engineA->lib, (const FT_Byte *)src->buf.data(), src->buf.size(), faceIndexA, &faceA)) { return nullptr; } } - return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, codeToGIDLenA, false, false); + return new SplashFTFontFile(engineA, std::move(idA), src, faceA, std::move(codeToGIDA), false, false); } -SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA, int faceIndexA) +SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGIDA, int faceIndexA) { FT_Face faceA; @@ -99,15 +100,14 @@ SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA, } } - return new SplashFTFontFile(engineA, idA, src, faceA, codeToGIDA, codeToGIDLenA, true, false); + return new SplashFTFontFile(engineA, std::move(idA), src, faceA, std::move(codeToGIDA), true, false); } -SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *srcA, FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, bool trueTypeA, bool type1A) : SplashFontFile(idA, srcA) +SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *srcA, FT_Face faceA, std::vector &&codeToGIDA, bool trueTypeA, bool type1A) : SplashFontFile(std::move(idA), srcA) { engine = engineA; face = faceA; - codeToGID = codeToGIDA; - codeToGIDLen = codeToGIDLenA; + codeToGID = std::move(codeToGIDA); trueType = trueTypeA; type1 = type1A; } @@ -117,9 +117,6 @@ SplashFTFontFile::~SplashFTFontFile() if (face) { FT_Done_Face(face); } - if (codeToGID) { - gfree(codeToGID); - } } SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat, const SplashCoord *textMat) diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h index 3e52dd8..1419a49 100644 --- a/splash/SplashFTFontFile.h +++ b/splash/SplashFTFontFile.h @@ -14,7 +14,8 @@ // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2017, 2018 Oliver Sander // Copyright (C) 2017 Adrian Johnson -// Copyright (C) 2019 Albert Astals Cid +// Copyright (C) 2019, 2024 Albert Astals Cid +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -24,6 +25,8 @@ #ifndef SPLASHFTFONTFILE_H #define SPLASHFTFONTFILE_H +#include + #include #include FT_FREETYPE_H #include "SplashFontFile.h" @@ -38,9 +41,9 @@ class SplashFTFontEngine; class SplashFTFontFile : public SplashFontFile { public: - static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, const char **encA); - static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA); - static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, int *codeToGIDA, int codeToGIDLenA, int faceIndexA = 0); + static SplashFontFile *loadType1Font(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, const char **encA, int faceIndexA); + static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGIDA, int faceIndexA); + static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGIDA, int faceIndexA); ~SplashFTFontFile() override; @@ -49,12 +52,11 @@ class SplashFTFontFile : public SplashFontFile SplashFont *makeFont(SplashCoord *mat, const SplashCoord *textMat) override; private: - SplashFTFontFile(SplashFTFontEngine *engineA, SplashFontFileID *idA, SplashFontSrc *src, FT_Face faceA, int *codeToGIDA, int codeToGIDLenA, bool trueTypeA, bool type1A); + SplashFTFontFile(SplashFTFontEngine *engineA, std::unique_ptr idA, SplashFontSrc *src, FT_Face faceA, std::vector &&codeToGIDA, bool trueTypeA, bool type1A); SplashFTFontEngine *engine; FT_Face face; - int *codeToGID; - int codeToGIDLen; + std::vector codeToGID; bool trueType; bool type1; diff --git a/splash/SplashFont.h b/splash/SplashFont.h index 8c54b08..9d6ac3d 100644 --- a/splash/SplashFont.h +++ b/splash/SplashFont.h @@ -11,7 +11,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2007-2008, 2018, 2019 Albert Astals Cid +// Copyright (C) 2007-2008, 2018, 2019, 2024 Albert Astals Cid // Copyright (C) 2018 Oliver Sander // // To see a description of the changes please see the Changelog file that @@ -24,6 +24,7 @@ #include "SplashTypes.h" #include "SplashClip.h" +#include "poppler_private_export.h" struct SplashGlyphBitmap; struct SplashFontCacheTag; @@ -42,7 +43,7 @@ class SplashPath; // SplashFont //------------------------------------------------------------------------ -class SplashFont +class POPPLER_PRIVATE_EXPORT SplashFont { public: SplashFont(SplashFontFile *fontFileA, const SplashCoord *matA, const SplashCoord *textMatA, bool aaA); diff --git a/splash/SplashFontEngine.cc b/splash/SplashFontEngine.cc index fb0d0a1..a168da2 100644 --- a/splash/SplashFontEngine.cc +++ b/splash/SplashFontEngine.cc @@ -14,13 +14,14 @@ // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2009 Petr Gajdos // Copyright (C) 2009 Kovid Goyal -// Copyright (C) 2009 Albert Astals Cid +// Copyright (C) 2009, 2024 Albert Astals Cid // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2015 Dmytro Morgun // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Oliver Sander // Copyright (C) 2019 Christian Persch +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -51,7 +52,7 @@ SplashFontEngine::SplashFontEngine(bool enableFreeType, bool enableFreeTypeHinting, bool enableSlightHinting, bool aa) { - std::fill(fontCache.begin(), fontCache.end(), nullptr); + std::ranges::fill(fontCache, nullptr); if (enableFreeType) { ftEngine = SplashFTFontEngine::init(aa, enableFreeTypeHinting, enableSlightHinting); @@ -66,17 +67,15 @@ SplashFontEngine::~SplashFontEngine() delete font; } - if (ftEngine) { - delete ftEngine; - } + delete ftEngine; } -SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) +SplashFontFile *SplashFontEngine::getFontFile(const SplashFontFileID &id) { for (auto font : fontCache) { if (font) { SplashFontFile *fontFile = font->getFontFile(); - if (fontFile && fontFile->getID()->matches(id)) { + if (fontFile && fontFile->getID().matches(id)) { return fontFile; } } @@ -84,12 +83,12 @@ SplashFontFile *SplashFontEngine::getFontFile(SplashFontFileID *id) return nullptr; } -SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFontEngine::loadType1Font(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadType1Font(idA, src, enc); + fontFile = ftEngine->loadType1Font(std::move(idA), src, enc, faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -103,12 +102,12 @@ SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA, SplashFon return fontFile; } -SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFontEngine::loadType1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadType1CFont(idA, src, enc); + fontFile = ftEngine->loadType1CFont(std::move(idA), src, enc, faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -122,12 +121,12 @@ SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA, SplashFo return fontFile; } -SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc) +SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadOpenTypeT1CFont(idA, src, enc); + fontFile = ftEngine->loadOpenTypeT1CFont(std::move(idA), src, enc, faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -141,12 +140,12 @@ SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA, Spl return fontFile; } -SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src) +SplashFontFile *SplashFontEngine::loadCIDFont(std::unique_ptr idA, SplashFontSrc *src, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadCIDFont(idA, src); + fontFile = ftEngine->loadCIDFont(std::move(idA), src, faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -160,12 +159,12 @@ SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA, SplashFontS return fontFile; } -SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen) +SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadOpenTypeCFFFont(idA, src, codeToGID, codeToGIDLen); + fontFile = ftEngine->loadOpenTypeCFFFont(std::move(idA), src, std::move(codeToGID), faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -179,16 +178,12 @@ SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA, Spl return fontFile; } -SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex) +SplashFontFile *SplashFontEngine::loadTrueTypeFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex) { SplashFontFile *fontFile = nullptr; if (ftEngine) { - fontFile = ftEngine->loadTrueTypeFont(idA, src, codeToGID, codeToGIDLen, faceIndex); - } - - if (!fontFile) { - gfree(codeToGID); + fontFile = ftEngine->loadTrueTypeFont(std::move(idA), src, std::move(codeToGID), faceIndex); } // delete the (temporary) font file -- with Unix hard link @@ -231,7 +226,7 @@ SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, const SplashCoor } // Try to find the font in the cache - auto fontIt = std::find_if(fontCache.begin(), fontCache.end(), [&](const SplashFont *font) { return font && font->matches(fontFile, mat, textMat); }); + auto fontIt = std::ranges::find_if(fontCache, [&](const SplashFont *font) { return font && font->matches(fontFile, mat, textMat); }); // The requested font has been found in the cache if (fontIt != fontCache.end()) { @@ -244,7 +239,7 @@ SplashFont *SplashFontEngine::getFont(SplashFontFile *fontFile, const SplashCoor if (fontCache.back()) { delete fontCache.back(); } - std::rotate(fontCache.begin(), fontCache.end() - 1, fontCache.end()); + std::ranges::rotate(fontCache, fontCache.end() - 1); fontCache[0] = newFont; return fontCache[0]; diff --git a/splash/SplashFontEngine.h b/splash/SplashFontEngine.h index 3e5458c..3024039 100644 --- a/splash/SplashFontEngine.h +++ b/splash/SplashFontEngine.h @@ -13,11 +13,12 @@ // // Copyright (C) 2006 Takashi Iwai // Copyright (C) 2009 Petr Gajdos -// Copyright (C) 2009, 2011, 2018 Albert Astals Cid +// Copyright (C) 2009, 2011, 2018, 2024 Albert Astals Cid // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2017 Adrian Johnson // Copyright (C) 2018 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -28,6 +29,8 @@ #define SPLASHFONTENGINE_H #include +#include +#include #include "SplashTypes.h" #include "poppler_private_export.h" @@ -58,15 +61,15 @@ class POPPLER_PRIVATE_EXPORT SplashFontEngine // Get a font file from the cache. Returns NULL if there is no // matching entry in the cache. - SplashFontFile *getFontFile(SplashFontFileID *id); + SplashFontFile *getFontFile(const SplashFontFileID &id); // Load fonts - these create new SplashFontFile objects. - SplashFontFile *loadType1Font(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadType1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, SplashFontSrc *src, const char **enc); - SplashFontFile *loadCIDFont(SplashFontFileID *idA, SplashFontSrc *src); - SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen); - SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, SplashFontSrc *src, int *codeToGID, int codeToGIDLen, int faceIndex = 0); + SplashFontFile *loadType1Font(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadType1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadOpenTypeT1CFont(std::unique_ptr idA, SplashFontSrc *src, const char **enc, int faceIndex); + SplashFontFile *loadCIDFont(std::unique_ptr idA, SplashFontSrc *src, int faceIndex); + SplashFontFile *loadOpenTypeCFFFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex); + SplashFontFile *loadTrueTypeFont(std::unique_ptr idA, SplashFontSrc *src, std::vector &&codeToGID, int faceIndex); // Get a font - this does a cache lookup first, and if not found, // creates a new SplashFont object and adds it to the cache. The diff --git a/splash/SplashFontFile.cc b/splash/SplashFontFile.cc index c3eace4..bf0c8c3 100644 --- a/splash/SplashFontFile.cc +++ b/splash/SplashFontFile.cc @@ -15,6 +15,7 @@ // Copyright (C) 2008, 2022 Albert Astals Cid // Copyright (C) 2019 Christian Persch // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -27,8 +28,6 @@ #ifdef HAVE_UNISTD_H # include #endif -#include "goo/gmem.h" -#include "goo/GooString.h" #include "SplashFontFile.h" #include "SplashFontFileID.h" @@ -36,9 +35,9 @@ // SplashFontFile //------------------------------------------------------------------------ -SplashFontFile::SplashFontFile(SplashFontFileID *idA, SplashFontSrc *srcA) +SplashFontFile::SplashFontFile(std::unique_ptr idA, SplashFontSrc *srcA) { - id = idA; + id = std::move(idA); src = srcA; src->ref(); refCnt = 0; @@ -48,7 +47,6 @@ SplashFontFile::SplashFontFile(SplashFontFileID *idA, SplashFontSrc *srcA) SplashFontFile::~SplashFontFile() { src->unref(); - delete id; } void SplashFontFile::incRefCnt() diff --git a/splash/SplashFontFile.h b/splash/SplashFontFile.h index 7f55fdf..3ee27ce 100644 --- a/splash/SplashFontFile.h +++ b/splash/SplashFontFile.h @@ -12,8 +12,9 @@ // under GPL version 2 or later // // Copyright (C) 2006 Takashi Iwai -// Copyright (C) 2008, 2010, 2018 Albert Astals Cid +// Copyright (C) 2008, 2010, 2018, 2024 Albert Astals Cid // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -25,6 +26,7 @@ #include #include +#include #include "SplashTypes.h" #include "poppler_private_export.h" @@ -61,7 +63,7 @@ class POPPLER_PRIVATE_EXPORT SplashFontSrc int refcnt; }; -class SplashFontFile +class POPPLER_PRIVATE_EXPORT SplashFontFile { public: virtual ~SplashFontFile(); @@ -74,7 +76,7 @@ class SplashFontFile virtual SplashFont *makeFont(SplashCoord *mat, const SplashCoord *textMat) = 0; // Get the font file ID. - SplashFontFileID *getID() { return id; } + const SplashFontFileID &getID() const { return *id; } // Increment the reference count. void incRefCnt(); @@ -86,9 +88,9 @@ class SplashFontFile bool doAdjustMatrix; protected: - SplashFontFile(SplashFontFileID *idA, SplashFontSrc *srcA); + SplashFontFile(std::unique_ptr idA, SplashFontSrc *srcA); - SplashFontFileID *id; + std::unique_ptr id; SplashFontSrc *src; int refCnt; diff --git a/splash/SplashFontFileID.cc b/splash/SplashFontFileID.cc index 6eb296c..919299d 100644 --- a/splash/SplashFontFileID.cc +++ b/splash/SplashFontFileID.cc @@ -12,6 +12,6 @@ // SplashFontFileID //------------------------------------------------------------------------ -SplashFontFileID::SplashFontFileID() { } +SplashFontFileID::SplashFontFileID() = default; -SplashFontFileID::~SplashFontFileID() { } +SplashFontFileID::~SplashFontFileID() = default; diff --git a/splash/SplashFontFileID.h b/splash/SplashFontFileID.h index c127dd8..fc145a2 100644 --- a/splash/SplashFontFileID.h +++ b/splash/SplashFontFileID.h @@ -12,6 +12,7 @@ // under GPL version 2 or later // // Copyright (C) 2018 Albert Astals Cid +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -34,7 +35,7 @@ class POPPLER_PRIVATE_EXPORT SplashFontFileID virtual ~SplashFontFileID(); SplashFontFileID(const SplashFontFileID &) = delete; SplashFontFileID &operator=(const SplashFontFileID &) = delete; - virtual bool matches(SplashFontFileID *id) = 0; + virtual bool matches(const SplashFontFileID &id) const = 0; }; #endif diff --git a/splash/SplashPattern.cc b/splash/SplashPattern.cc index 0dae246..2323bd8 100644 --- a/splash/SplashPattern.cc +++ b/splash/SplashPattern.cc @@ -29,9 +29,9 @@ // SplashPattern //------------------------------------------------------------------------ -SplashPattern::SplashPattern() { } +SplashPattern::SplashPattern() = default; -SplashPattern::~SplashPattern() { } +SplashPattern::~SplashPattern() = default; //------------------------------------------------------------------------ // SplashSolidColor @@ -42,7 +42,7 @@ SplashSolidColor::SplashSolidColor(SplashColorConstPtr colorA) splashColorCopy(color, colorA); } -SplashSolidColor::~SplashSolidColor() { } +SplashSolidColor::~SplashSolidColor() = default; bool SplashSolidColor::getColor(int x, int y, SplashColorPtr c) { diff --git a/splash/SplashState.h b/splash/SplashState.h index 009c1f1..1648e32 100644 --- a/splash/SplashState.h +++ b/splash/SplashState.h @@ -13,7 +13,7 @@ // // Copyright (C) 2011, 2012, 2015 Thomas Freitag // Copyright (C) 2017 Adrian Johnson -// Copyright (C) 2018, 2021, 2022 Albert Astals Cid +// Copyright (C) 2018, 2021, 2022, 2024 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -25,6 +25,8 @@ #include "SplashTypes.h" +#include + class SplashPattern; class SplashScreen; class SplashClip; diff --git a/splash/SplashXPath.cc b/splash/SplashXPath.cc index d066915..5648bed 100644 --- a/splash/SplashXPath.cc +++ b/splash/SplashXPath.cc @@ -51,7 +51,7 @@ struct SplashXPathAdjust //------------------------------------------------------------------------ // Transform a point from user space to device space. -inline void SplashXPath::transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) +inline void SplashXPath::transform(const SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo) { // [ m[0] m[1] 0 ] // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ] diff --git a/splash/SplashXPath.h b/splash/SplashXPath.h index e59649f..c004fc0 100644 --- a/splash/SplashXPath.h +++ b/splash/SplashXPath.h @@ -78,7 +78,7 @@ class SplashXPath void sort(); protected: - void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); + void transform(const SplashCoord *matrix, SplashCoord xi, SplashCoord yi, SplashCoord *xo, SplashCoord *yo); void strokeAdjust(SplashXPathAdjust *adjust, SplashCoord *xp, SplashCoord *yp); void grow(int nSegs); void addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord flatness, bool first, bool last, bool end0, bool end1); diff --git a/splash/SplashXPathScanner.cc b/splash/SplashXPathScanner.cc index cc5f850..3d7db3a 100644 --- a/splash/SplashXPathScanner.cc +++ b/splash/SplashXPathScanner.cc @@ -11,7 +11,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2008, 2010, 2014, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2008, 2010, 2014, 2018, 2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2010 Paweł Wiejacha // Copyright (C) 2013, 2014, 2021 Thomas Freitag // Copyright (C) 2018 Stefan Brüns @@ -112,7 +112,7 @@ SplashXPathScanner::SplashXPathScanner(const SplashXPath &xPath, bool eoA, int c computeIntersections(xPath); } -SplashXPathScanner::~SplashXPathScanner() { } +SplashXPathScanner::~SplashXPathScanner() = default; void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA) const { @@ -318,7 +318,7 @@ void SplashXPathScanner::computeIntersections(const SplashXPath &xPath) } } for (auto &line : allIntersections) { - std::sort(line.begin(), line.end(), [](const SplashIntersect i0, const SplashIntersect i1) { return i0.x0 < i1.x0; }); + std::ranges::sort(line, [](const SplashIntersect i0, const SplashIntersect i1) { return i0.x0 < i1.x0; }); } } @@ -429,7 +429,7 @@ void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int *x1 = (xxMax - 1) / splashAASize; } -void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) const +void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf, const int *x0, const int *x1, int y) const { int xx0, xx1, xx, yy, yyMin, yyMax, interCount; size_t interIdx; diff --git a/splash/SplashXPathScanner.h b/splash/SplashXPathScanner.h index 476e98c..23d93f5 100644 --- a/splash/SplashXPathScanner.h +++ b/splash/SplashXPathScanner.h @@ -91,7 +91,7 @@ class SplashXPathScanner // Clips an anti-aliased line by setting pixels to zero. On entry, // all non-zero pixels are between and . This function // will update and . - void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) const; + void clipAALine(SplashBitmap *aaBuf, const int *x0, const int *x1, int y) const; private: void computeIntersections(const SplashXPath &xPath); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6243c56..afa1352 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,6 +23,7 @@ if (GTK_FOUND) ) poppler_add_test(gtk-test BUILD_GTK_TESTS ${gtk_splash_test_SRCS}) target_link_libraries(gtk-test ${CAIRO_LIBRARIES} poppler-glib PkgConfig::GTK3) + target_include_directories(gtk-test SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) if (HAVE_CAIRO) @@ -34,17 +35,13 @@ if (GTK_FOUND) ) poppler_add_test(pdf-inspector BUILD_GTK_TESTS ${pdf_inspector_SRCS}) target_link_libraries(pdf-inspector ${CAIRO_LIBRARIES} Freetype::Freetype ${common_libs} PkgConfig::GTK3 poppler) + target_include_directories(pdf-inspector SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) target_compile_definitions(pdf-inspector PRIVATE -DSRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}") endif () endif () if (HAVE_CAIRO) - include_directories( - SYSTEM - ${CAIRO_INCLUDE_DIRS} - ) - include(CheckCXXSymbolExists) set (CMAKE_REQUIRED_INCLUDES ${CAIRO_INCLUDE_DIRS}) check_cxx_symbol_exists(CAIRO_HAS_PNG_FUNCTIONS "cairo.h" HAVE_CAIRO_PNG) @@ -62,6 +59,7 @@ if (HAVE_CAIRO) ) add_executable(cairo-thread-test ${cairo_thread_test_SRCS}) target_link_libraries(cairo-thread-test ${CAIRO_LIBRARIES} Freetype::Freetype Threads::Threads poppler) + target_include_directories(cairo-thread-test SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) endif () endif () diff --git a/test/cairo-thread-test.cc b/test/cairo-thread-test.cc index 46e08ed..fa85549 100644 --- a/test/cairo-thread-test.cc +++ b/test/cairo-thread-test.cc @@ -4,7 +4,7 @@ // // This file is licensed under the GPLv2 or later // -// Copyright (C) 2022 Adrian Johnson +// Copyright (C) 2022, 2024 Adrian Johnson // //======================================================================== @@ -490,7 +490,7 @@ int main(int argc, char *argv[]) std::vector threads; threads.reserve(4); for (int i = 0; i < numThreads; i++) { - threads.emplace_back(std::thread(runThread, jobQueue)); + threads.emplace_back(runThread, jobQueue); } std::vector inputFiles; @@ -557,5 +557,12 @@ int main(int argc, char *argv[]) threads[i].join(); } +#ifndef NDEBUG + // Clear the cairo font cache. If all references to font faces or + // scaled fonts have not been released this function will + // assert. If this occurs we have found a memory leak. + cairo_debug_reset_static_data(); +#endif + return 0; } diff --git a/test/gtk-test.cc b/test/gtk-test.cc index 1507b0e..119f6f1 100644 --- a/test/gtk-test.cc +++ b/test/gtk-test.cc @@ -31,7 +31,7 @@ static const GOptionEntry options[] = { { "cairo", 'c', 0, G_OPTION_ARG_NONE, &c #ifndef G_OS_WIN32 { "fd", 'f', 0, G_OPTION_ARG_NONE, &args_are_fds, "File descriptors", nullptr }, #endif - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, nullptr, "PDF-FILES…" }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, static_cast(&file_arguments), nullptr, "PDF-FILES…" }, {} }; static GList *view_list = nullptr; @@ -90,7 +90,7 @@ typedef struct // Constants and macros //------------------------------------------------------------------------ -#define xoutRound(x) ((int)(x + 0.5)) +#define xoutRound(x) ((int)((x) + 0.5)) //------------------------------------------------------------------------ // GDKSplashOutputDev @@ -102,7 +102,7 @@ GDKSplashOutputDev::GDKSplashOutputDev(GdkScreen *screen, void (*redrawCbkA)(voi redrawCbkData = redrawCbkDataA; } -GDKSplashOutputDev::~GDKSplashOutputDev() { } +GDKSplashOutputDev::~GDKSplashOutputDev() = default; void GDKSplashOutputDev::clear() { @@ -284,11 +284,7 @@ static View *view_new(PopplerDocument *doc) view->drawing_area = gtk_drawing_area_new(); sw = gtk_scrolled_window_new(nullptr, nullptr); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); -#if GTK_CHECK_VERSION(3, 7, 8) gtk_container_add(GTK_CONTAINER(sw), view->drawing_area); -#else - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), view->drawing_area); -#endif gtk_widget_show(view->drawing_area); gtk_box_pack_end(GTK_BOX(vbox), sw, TRUE, TRUE, 0); diff --git a/test/pdf-fullrewrite.cc b/test/pdf-fullrewrite.cc index 6739da4..b5356ef 100644 --- a/test/pdf-fullrewrite.cc +++ b/test/pdf-fullrewrite.cc @@ -5,6 +5,7 @@ // Copyright 2007 Julien Rebetez // Copyright 2012 Fabio D'Urso // Copyright 2022 Albert Astals Cid +// Copyright 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -203,8 +204,8 @@ static bool compareObjects(const Object *objA, const Object *objB) return false; } else { int c; - streamA->reset(); - streamB->reset(); + (void)streamA->reset(); + (void)streamB->reset(); do { c = streamA->getChar(); if (c != streamB->getChar()) { diff --git a/test/pdf-inspector.cc b/test/pdf-inspector.cc index 7fae242..0dd0663 100644 --- a/test/pdf-inspector.cc +++ b/test/pdf-inspector.cc @@ -27,7 +27,7 @@ #include // Mapping -#include "pdf-operators.c" +#include "pdf-operators.h" enum { diff --git a/test/pdf-operators.c b/test/pdf-operators.h similarity index 100% rename from test/pdf-operators.c rename to test/pdf-operators.h diff --git a/test/perf-test.cc b/test/perf-test.cc index 3dda7a2..501f535 100644 --- a/test/perf-test.cc +++ b/test/perf-test.cc @@ -3,6 +3,8 @@ Copyright 2018, 2020, 2022 Albert Astals Cid 2018 Copyright 2019 Oliver Sander Copyright 2020 Adam Reichold + Copyright 2024 Vincent Lefevre + Copyright 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela License: GPLv2 */ /* A tool to stress-test poppler rendering and measure rendering times for @@ -35,7 +37,7 @@ // before it's tested. This is desired if a file is on a slow drive. // Currently copying only works on Windows. // Not enabled by default. -//#define COPY_FILE 1 +// #define COPY_FILE 1 #include #include @@ -69,7 +71,7 @@ #define dimof(X) (sizeof(X) / sizeof((X)[0])) -#define INVALID_PAGE_NO -1 +#define INVALID_PAGE_NO (-1) /* Those must be implemented in order to provide preview during execution. They can be no-ops. An implementation for windows is in @@ -176,7 +178,7 @@ static bool gfSlowPreview = false; /* If true, we only dump the text, not render */ static bool gfTextOnly = false; -#define PAGE_NO_NOT_GIVEN -1 +#define PAGE_NO_NOT_GIVEN (-1) /* If equals PAGE_NO_NOT_GIVEN, we're in default mode where we render all pages. If different, will only render this page */ @@ -338,7 +340,6 @@ static void sleep_milliseconds(int milliseconds) return; } } - return; #endif } @@ -350,11 +351,11 @@ static SplashColor splashColBlue; static SplashColor splashColWhite; static SplashColor splashColBlack; -#define SPLASH_COL_RED_PTR (SplashColorPtr) & (splashColRed[0]) -#define SPLASH_COL_GREEN_PTR (SplashColorPtr) & (splashColGreen[0]) -#define SPLASH_COL_BLUE_PTR (SplashColorPtr) & (splashColBlue[0]) -#define SPLASH_COL_WHITE_PTR (SplashColorPtr) & (splashColWhite[0]) -#define SPLASH_COL_BLACK_PTR (SplashColorPtr) & (splashColBlack[0]) +#define SPLASH_COL_RED_PTR ((SplashColorPtr) & (splashColRed[0])) +#define SPLASH_COL_GREEN_PTR ((SplashColorPtr) & (splashColGreen[0])) +#define SPLASH_COL_BLUE_PTR ((SplashColorPtr) & (splashColBlue[0])) +#define SPLASH_COL_WHITE_PTR ((SplashColorPtr) & (splashColWhite[0])) +#define SPLASH_COL_BLACK_PTR ((SplashColorPtr) & (splashColBlack[0])) static SplashColorPtr gBgColor = SPLASH_COL_WHITE_PTR; @@ -608,7 +609,6 @@ static bool ShowPreview() static void RenderPdfAsText(const char *fileName) { PDFDoc *pdfDoc = nullptr; - GooString *txt = nullptr; int pageCount; double timeInMs; @@ -628,7 +628,7 @@ static void RenderPdfAsText(const char *fileName) GooTimer msTimer; pdfDoc = new PDFDoc(std::make_unique(fileName)); if (!pdfDoc->isOk()) { - error(errIO, -1, "RenderPdfFile(): failed to open PDF file {0:s}\n", fileName); + error(errIO, -1, "RenderPdfFile(): failed to open PDF file {0:s}", fileName); goto Exit; } @@ -650,15 +650,13 @@ static void RenderPdfAsText(const char *fileName) bool crop = true; bool doLinks = false; pdfDoc->displayPage(textOut, curPage, 72, 72, rotate, useMediaBox, crop, doLinks); - txt = textOut->getText(0.0, 0.0, 10000.0, 10000.0); + GooString txt = textOut->getText(0.0, 0.0, 10000.0, 10000.0); msTimer.stop(); timeInMs = msTimer.getElapsed(); if (gfTimings) { LogInfo("page %d: %.2f ms\n", curPage, timeInMs); } - printf("%s\n", txt->c_str()); - delete txt; - txt = nullptr; + printf("%s\n", txt.c_str()); } Exit: diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 747fb1e..3b56650 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,3 +1,5 @@ +include(FindGettext) +include(FindIntl) set(common_srcs parseargs.cc @@ -16,6 +18,7 @@ add_executable(pdftoppm ${pdftoppm_SOURCES}) target_link_libraries(pdftoppm ${common_libs}) if(LCMS2_FOUND) target_link_libraries(pdftoppm ${LCMS2_LIBRARIES}) + target_include_directories(pdftoppm SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) endif() install(TARGETS pdftoppm DESTINATION bin) install(FILES pdftoppm.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -32,14 +35,14 @@ if (HAVE_CAIRO) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - SYSTEM - ${CAIRO_INCLUDE_DIRS} ) add_definitions(${CAIRO_CFLAGS}) add_executable(pdftocairo ${pdftocairo_SOURCES}) target_link_libraries(pdftocairo ${CAIRO_LIBRARIES} Freetype::Freetype ${common_libs}) + target_include_directories(pdftocairo SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) if(LCMS2_FOUND) target_link_libraries(pdftocairo ${LCMS2_LIBRARIES}) + target_include_directories(pdftocairo SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) endif() install(TARGETS pdftocairo DESTINATION bin) install(FILES pdftocairo.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) @@ -92,14 +95,25 @@ target_link_libraries(pdfinfo ${common_libs}) install(TARGETS pdfinfo DESTINATION bin) install(FILES pdfinfo.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) -if (ENABLE_NSS3) +if (GETTEXT_FOUND AND Intl_FOUND AND ENABLE_SIGNATURES) + add_definitions(-DHAVE_GETTEXT) + add_definitions(-DCMAKE_INSTALL_LOCALEDIR="${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}") + add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/po") +endif () + +if (ENABLE_SIGNATURES) # pdfsig set(pdfsig_SOURCES ${common_srcs} pdfsig.cc ) add_executable(pdfsig ${pdfsig_SOURCES}) - target_include_directories(pdfsig SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) target_link_libraries(pdfsig ${common_libs}) + if (ENABLE_NSS3) + target_include_directories(pdfsig SYSTEM PRIVATE ${NSS3_INCLUDE_DIRS}) + endif() + if (Intl_FOUND) + target_link_libraries(pdfsig Intl::Intl) + endif () install(TARGETS pdfsig DESTINATION bin) install(FILES pdfsig.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () @@ -113,6 +127,7 @@ add_executable(pdftops ${pdftops_SOURCES}) target_link_libraries(pdftops ${common_libs}) if(LCMS2_FOUND) target_link_libraries(pdftops ${LCMS2_LIBRARIES}) + target_include_directories(pdftops SYSTEM PRIVATE ${LCMS2_INCLUDE_DIR}) endif() install(TARGETS pdftops DESTINATION bin) install(FILES pdftops.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) diff --git a/utils/HtmlFonts.cc b/utils/HtmlFonts.cc index ca7d4a4..a9cfb97 100644 --- a/utils/HtmlFonts.cc +++ b/utils/HtmlFonts.cc @@ -32,6 +32,7 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2022 Oliver Sander // Copyright (C) 2020 Eddie Kohler +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -66,7 +67,7 @@ void removeStyleSuffix(std::string &familyName) } -#define xoutRound(x) ((int)(x + 0.5)) +#define xoutRound(x) ((int)((x) + 0.5)) extern bool xml; extern bool fontFullName; @@ -156,7 +157,7 @@ HtmlFont::HtmlFont(const GfxFont &font, int _size, GfxRGB rgb, double opacity) italic = true; } - familyName = fontname->c_str(); + familyName = *fontname; removeStyleSuffix(familyName); } else { FontName = new GooString(defaultFamilyName); @@ -230,9 +231,9 @@ GooString *HtmlFont::getFullName() } // this method if plain wrong todo -GooString *HtmlFont::HtmlFilter(const Unicode *u, int uLen) +std::unique_ptr HtmlFont::HtmlFilter(const Unicode *u, int uLen) { - GooString *tmp = new GooString(); + auto tmp = std::make_unique(); const UnicodeMap *uMap; char buf[8]; int n; @@ -278,9 +279,9 @@ GooString *HtmlFont::HtmlFilter(const Unicode *u, int uLen) return tmp; } -HtmlFontAccu::HtmlFontAccu() { } +HtmlFontAccu::HtmlFontAccu() = default; -HtmlFontAccu::~HtmlFontAccu() { } +HtmlFontAccu::~HtmlFontAccu() = default; int HtmlFontAccu::AddFont(const HtmlFont &font) { diff --git a/utils/HtmlFonts.h b/utils/HtmlFonts.h index ca4ae54..0cae926 100644 --- a/utils/HtmlFonts.h +++ b/utils/HtmlFonts.h @@ -25,6 +25,7 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2020 Eddie Kohler // Copyright (C) 2022 Oliver Sander +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -51,25 +52,9 @@ class HtmlFontColor public: HtmlFontColor() : r(0), g(0), b(0), opacity(255) { } HtmlFontColor(GfxRGB rgb, double opacity); - HtmlFontColor(const HtmlFontColor &x) - { - r = x.r; - g = x.g; - b = x.b; - opacity = x.opacity; - } - HtmlFontColor &operator=(const HtmlFontColor &x) - { - r = x.r; - g = x.g; - b = x.b; - opacity = x.opacity; - return *this; - } - ~HtmlFontColor() {}; GooString *toString() const; double getOpacity() const { return opacity / 255.0; } - bool isEqual(const HtmlFontColor &col) const { return ((r == col.r) && (g == col.g) && (b == col.b) && (opacity == col.opacity)); } + bool isEqual(HtmlFontColor col) const { return ((r == col.r) && (g == col.g) && (b == col.b) && (opacity == col.opacity)); } }; class HtmlFont @@ -104,7 +89,7 @@ class HtmlFont } const double *getRotMat() const { return rotSkewMat; } GooString *getFontName(); - static GooString *HtmlFilter(const Unicode *u, int uLen); // char* s); + static std::unique_ptr HtmlFilter(const Unicode *u, int uLen); // char* s); bool isEqual(const HtmlFont &x) const; bool isEqualIgnoreBold(const HtmlFont &x) const; void print() const { printf("font: %s (%s) %d %s%s\n", FontName->c_str(), familyName.c_str(), size, bold ? "bold " : "", italic ? "italic " : ""); }; diff --git a/utils/HtmlLinks.cc b/utils/HtmlLinks.cc index a28d23d..e80bfe1 100644 --- a/utils/HtmlLinks.cc +++ b/utils/HtmlLinks.cc @@ -18,7 +18,7 @@ // under GPL version 2 or later // // Copyright (C) 2008 Boris Toloknov -// Copyright (C) 2010, 2021, 2022 Albert Astals Cid +// Copyright (C) 2010, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2013 Julien Nabet // // To see a description of the changes please see the Changelog file that @@ -36,10 +36,10 @@ HtmlLink::HtmlLink(const HtmlLink &x) Ymin = x.Ymin; Xmax = x.Xmax; Ymax = x.Ymax; - dest = new GooString(x.dest); + dest = std::make_unique(x.dest.get()); } -HtmlLink::HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_dest) +HtmlLink::HtmlLink(double xmin, double ymin, double xmax, double ymax, std::unique_ptr &&_dest) : dest(std::move(_dest)) { if (xmin < xmax) { Xmin = xmin; @@ -55,13 +55,9 @@ HtmlLink::HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString Ymin = ymax; Ymax = ymin; } - dest = new GooString(_dest); } -HtmlLink::~HtmlLink() -{ - delete dest; -} +HtmlLink::~HtmlLink() = default; bool HtmlLink::isEqualDest(const HtmlLink &x) const { @@ -113,12 +109,12 @@ static GooString *EscapeSpecialChars(GooString *s) return tmp ? tmp : s; } -GooString *HtmlLink::getLinkStart() const +std::unique_ptr HtmlLink::getLinkStart() const { - GooString *res = new GooString(" res = std::make_unique("append(d); - if (d != dest) { + if (d != dest.get()) { delete d; } res->append("\">"); @@ -136,9 +132,9 @@ GooString *HtmlLink::getLinkStart() const return tmp; }*/ -HtmlLinks::HtmlLinks() { } +HtmlLinks::HtmlLinks() = default; -HtmlLinks::~HtmlLinks() { } +HtmlLinks::~HtmlLinks() = default; bool HtmlLinks::inLink(double xmin, double ymin, double xmax, double ymax, size_t &p) const { diff --git a/utils/HtmlLinks.h b/utils/HtmlLinks.h index 0e62f56..17e4ed1 100644 --- a/utils/HtmlLinks.h +++ b/utils/HtmlLinks.h @@ -17,7 +17,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2010, 2018, 2021, 2022 Albert Astals Cid +// Copyright (C) 2010, 2018, 2021, 2022, 2024 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -40,22 +40,20 @@ class HtmlLink double Ymin; double Xmax; double Ymax; - GooString *dest; + std::unique_ptr dest; public: HtmlLink(const HtmlLink &x); - HtmlLink(double xmin, double ymin, double xmax, double ymax, GooString *_dest); + HtmlLink(double xmin, double ymin, double xmax, double ymax, std::unique_ptr &&_dest); ~HtmlLink(); HtmlLink &operator=(const HtmlLink &) = delete; bool isEqualDest(const HtmlLink &x) const; - GooString *getDest() const { return new GooString(dest); } double getX1() const { return Xmin; } double getX2() const { return Xmax; } double getY1() const { return Ymin; } double getY2() const { return Ymax; } bool inLink(double xmin, double ymin, double xmax, double ymax) const; - // GooString *Link(GooString *content); - GooString *getLinkStart() const; + std::unique_ptr getLinkStart() const; }; class HtmlLinks diff --git a/utils/HtmlOutputDev.cc b/utils/HtmlOutputDev.cc index 2611e06..2390bc8 100644 --- a/utils/HtmlOutputDev.cc +++ b/utils/HtmlOutputDev.cc @@ -17,7 +17,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005-2013, 2016-2022 Albert Astals Cid +// Copyright (C) 2005-2013, 2016-2022, 2024 Albert Astals Cid // Copyright (C) 2008 Kjartan Maraas // Copyright (C) 2008 Boris Toloknov // Copyright (C) 2008 Haruyuki Kawabe @@ -43,10 +43,11 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Thibaut Brard // Copyright (C) 2018-2020 Adam Reichold -// Copyright (C) 2019, 2020, 2022 Oliver Sander +// Copyright (C) 2019, 2020, 2022, 2024 Oliver Sander // Copyright (C) 2020 Eddie Kohler // Copyright (C) 2021 Christopher Hasse // Copyright (C) 2022 Brian Rosenfield +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -64,14 +65,13 @@ #include "goo/GooString.h" #include "goo/gbasename.h" #include "goo/gbase64.h" -#include "goo/gbasename.h" #include "UnicodeMap.h" #include "goo/gmem.h" #include "Error.h" #include "GfxState.h" #include "Page.h" #include "Annot.h" -#include "PNGWriter.h" +#include "goo/PNGWriter.h" #include "GlobalParams.h" #include "HtmlOutputDev.h" #include "HtmlFonts.h" @@ -85,7 +85,7 @@ class HtmlImage { public: - HtmlImage(std::unique_ptr &&_fName, GfxState *state) : fName(std::move(_fName)) + HtmlImage(std::string &&_fName, GfxState *state) : fName(std::move(_fName)) { state->transform(0, 0, &xMin, &yMax); state->transform(1, 1, &xMax, &yMin); @@ -96,7 +96,7 @@ class HtmlImage double xMin, xMax; // image x coordinates double yMin, yMax; // image y coordinates - std::unique_ptr fName; // image file name + std::string fName; // image file name }; // returns true if x is closer to y than x is to z @@ -124,30 +124,30 @@ static bool debug = false; #if 0 static GooString* Dirname(GooString* str){ - + char *p=str->c_str(); int len=str->getLength(); for (int i=len-1;i>=0;i--) - if (*(p+i)==SLASH) + if (*(p+i)==SLASH) return new GooString(p,i+1); return new GooString(); } #endif -static std::unique_ptr print_matrix(const double *mat) +static std::string print_matrix(const double *mat) { return GooString::format("[{0:g} {1:g} {2:g} {3:g} {4:g} {5:g}]", *mat, mat[1], mat[2], mat[3], mat[4], mat[5]); } -static std::unique_ptr print_uni_str(const Unicode *u, const unsigned uLen) +static std::string print_uni_str(const Unicode *u, const unsigned uLen) { if (!uLen) { - return std::make_unique(""); + return ""; } - std::unique_ptr gstr_buff0 = GooString::format("{0:c}", (*u < 0x7F ? *u & 0xFF : '?')); + std::string gstr_buff0 = GooString::format("{0:c}", (*u < 0x7F ? *u & 0xFF : '?')); for (unsigned i = 1; i < uLen; i++) { if (u[i] < 0x7F) { - gstr_buff0->append(u[i] < 0x7F ? static_cast(u[i]) & 0xFF : '?'); + gstr_buff0.push_back(static_cast(u[i]) & 0xFF); } } @@ -185,16 +185,16 @@ HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu *_fonts) : // browser rotates the opposite way // so flip the sign of the angle -> sin() components change sign if (debug) { - std::cerr << DEBUG << "before transform: " << print_matrix(normalizedMatrix)->c_str() << std::endl; + std::cerr << DEBUG << "before transform: " << print_matrix(normalizedMatrix) << std::endl; } normalizedMatrix[1] *= -1; normalizedMatrix[2] *= -1; if (debug) { - std::cerr << DEBUG << "after reflecting angle: " << print_matrix(normalizedMatrix)->c_str() << std::endl; + std::cerr << DEBUG << "after reflecting angle: " << print_matrix(normalizedMatrix) << std::endl; } normalizeRotMat(normalizedMatrix); if (debug) { - std::cerr << DEBUG << "after norm: " << print_matrix(normalizedMatrix)->c_str() << std::endl; + std::cerr << DEBUG << "after norm: " << print_matrix(normalizedMatrix) << std::endl; } hfont.setRotMat(normalizedMatrix); } @@ -219,14 +219,13 @@ HtmlString::HtmlString(GfxState *state, double fontSize, HtmlFontAccu *_fonts) : len = size = 0; yxNext = nullptr; xyNext = nullptr; - htext = new GooString(); + htext = std::make_unique(); dir = textDirUnknown; } HtmlString::~HtmlString() { gfree(text); - delete htext; gfree(xRight); } @@ -345,7 +344,6 @@ void HtmlPage::beginString(GfxState *state, const GooString *s) void HtmlPage::conv() { for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { - delete tmp->htext; tmp->htext = HtmlFont::HtmlFilter(tmp->text, tmp->len); size_t linkIndex = 0; @@ -372,8 +370,8 @@ void HtmlPage::addChar(GfxState *state, double x, double y, double dx, double dy // sin q is zero iff there is no rotation, or 180 deg. rotation; // for 180 rotation, cos q will be negative if (text_mat[0] < 0 || !is_within(text_mat[1], .1, 0)) { - std::cerr << DEBUG << "rotation matrix for \"" << print_uni_str(u, uLen)->c_str() << '"' << std::endl; - std::cerr << "text " << print_matrix(state->getTextMat())->c_str(); + std::cerr << DEBUG << "rotation matrix for \"" << print_uni_str(u, uLen) << '"' << std::endl; + std::cerr << "text " << print_matrix(state->getTextMat()); } } if (n > 0 && // don't start a new string, unless there is already a string @@ -551,9 +549,8 @@ void HtmlPage::coalesce() str1->htext->insert(0, "", 3); } if (str1->getLink() != nullptr) { - GooString *ls = str1->getLink()->getLinkStart(); - str1->htext->insert(0, ls); - delete ls; + const std::unique_ptr ls = str1->getLink()->getLinkStart(); + str1->htext->insert(0, ls.get()); } curX = str1->xMin; curY = str1->yMin; @@ -641,11 +638,10 @@ void HtmlPage::coalesce() bool finish_a = switch_links && hlink1 != nullptr; bool finish_italic = hfont1->isItalic() && (!hfont2->isItalic() || finish_a); bool finish_bold = hfont1->isBold() && (!hfont2->isBold() || finish_a || finish_italic); - CloseTags(str1->htext, finish_a, finish_italic, finish_bold); + CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold); if (switch_links && hlink2 != nullptr) { - GooString *ls = hlink2->getLinkStart(); - str1->htext->append(ls); - delete ls; + const std::unique_ptr ls = hlink2->getLinkStart(); + str1->htext->append(ls.get()); } if ((!hfont1->isItalic() || finish_italic) && hfont2->isItalic()) { str1->htext->append("", 3); @@ -654,7 +650,7 @@ void HtmlPage::coalesce() str1->htext->append("", 3); } - str1->htext->append(str2->htext); + str1->htext->append(str2->htext.get()); // str1 now contains href for link of str2 (if it is defined) str1->link = str2->link; hfont1 = hfont2; @@ -671,7 +667,7 @@ void HtmlPage::coalesce() bool finish_a = str1->getLink() != nullptr; bool finish_bold = hfont1->isBold(); bool finish_italic = hfont1->isItalic(); - CloseTags(str1->htext, finish_a, finish_italic, finish_bold); + CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold); str1->xMin = curX; str1->yMin = curY; @@ -686,9 +682,8 @@ void HtmlPage::coalesce() str1->htext->insert(0, "", 3); } if (str1->getLink() != nullptr) { - GooString *ls = str1->getLink()->getLinkStart(); - str1->htext->insert(0, ls); - delete ls; + const std::unique_ptr ls = str1->getLink()->getLinkStart(); + str1->htext->insert(0, ls.get()); } } } @@ -698,14 +693,14 @@ void HtmlPage::coalesce() bool finish_bold = hfont1->isBold(); bool finish_italic = hfont1->isItalic(); bool finish_a = str1->getLink() != nullptr; - CloseTags(str1->htext, finish_a, finish_italic, finish_bold); + CloseTags(str1->htext.get(), finish_a, finish_italic, finish_bold); #if 0 //~ for debugging for (str1 = yxStrings; str1; str1 = str1->yxNext) { printf("x=%3d..%3d y=%3d..%3d size=%2d ", (int)str1->xMin, (int)str1->xMax, (int)str1->yMin, (int)str1->yMax, (int)(str1->yMax - str1->yMin)); - printf("'%s'\n", str1->htext->c_str()); + printf("'%s'\n", str1->htext->c_str()); } printf("\n------------------------------------------------------------\n\n"); #endif @@ -713,7 +708,7 @@ void HtmlPage::coalesce() void HtmlPage::dumpAsXML(FILE *f, int page) { - fprintf(f, "\n", pageHeight, pageWidth); for (int i = fontsPageMarker; i < fonts->size(); i++) { @@ -725,13 +720,13 @@ void HtmlPage::dumpAsXML(FILE *f, int page) for (auto ptr : imgList) { auto img = static_cast(ptr); if (!noRoundedCoordinates) { - fprintf(f, "yMin), xoutRound(img->xMin)); - fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(img->xMax - img->xMin), xoutRound(img->yMax - img->yMin)); + fprintf(f, R"(yMin), xoutRound(img->xMin)); + fprintf(f, R"(width="%d" height="%d" )", xoutRound(img->xMax - img->xMin), xoutRound(img->yMax - img->yMin)); } else { - fprintf(f, "yMin, img->xMin); - fprintf(f, "width=\"%f\" height=\"%f\" ", img->xMax - img->xMin, img->yMax - img->yMin); + fprintf(f, R"(yMin, img->xMin); + fprintf(f, R"(width="%f" height="%f" )", img->xMax - img->xMin, img->yMax - img->yMin); } - fprintf(f, "src=\"%s\"/>\n", img->fName->c_str()); + fprintf(f, "src=\"%s\"/>\n", img->fName.c_str()); delete img; } imgList.clear(); @@ -739,11 +734,11 @@ void HtmlPage::dumpAsXML(FILE *f, int page) for (HtmlString *tmp = yxStrings; tmp; tmp = tmp->yxNext) { if (tmp->htext) { if (!noRoundedCoordinates) { - fprintf(f, "yMin), xoutRound(tmp->xMin)); - fprintf(f, "width=\"%d\" height=\"%d\" ", xoutRound(tmp->xMax - tmp->xMin), xoutRound(tmp->yMax - tmp->yMin)); + fprintf(f, R"(yMin), xoutRound(tmp->xMin)); + fprintf(f, R"(width="%d" height="%d" )", xoutRound(tmp->xMax - tmp->xMin), xoutRound(tmp->yMax - tmp->yMin)); } else { - fprintf(f, "yMin, tmp->xMin); - fprintf(f, "width=\"%f\" height=\"%f\" ", tmp->xMax - tmp->xMin, tmp->yMax - tmp->yMin); + fprintf(f, R"(yMin, tmp->xMin); + fprintf(f, R"(width="%f" height="%f" )", tmp->xMax - tmp->xMin, tmp->yMax - tmp->yMin); } fprintf(f, "font=\"%d\">", tmp->fontpos); fputs(tmp->htext->c_str(), f); @@ -893,7 +888,7 @@ void HtmlPage::dumpComplex(FILE *file, int page, const std::vector for (HtmlString *tmp1 = yxStrings; tmp1; tmp1 = tmp1->yxNext) { if (tmp1->htext) { - fprintf(pageFile, "

    yMin), xoutRound(tmp1->xMin)); + fprintf(pageFile, R"(


    \n", output); } else { - GooString *str = Docname->copy(); + std::unique_ptr str = Docname->copy(); str->append("-outline.html"); output = fopen(str->c_str(), "w"); - delete str; if (output == nullptr) { return false; } @@ -1723,10 +1710,11 @@ bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector\n", output); for (OutlineItem *item : *outlines) { - GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength()); + const auto &title = item->getTitle(); + std::unique_ptr titleStr = HtmlFont::HtmlFilter(title.data(), title.size()); GooString *linkName = nullptr; - ; + const int itemPage = getOutlinePageNum(item); if (itemPage > 0) { /* complex simple @@ -1753,12 +1741,13 @@ bool HtmlOutputDev::newHtmlOutlineLevel(FILE *output, const std::vector", linkName->c_str()); } - fputs(titleStr->c_str(), output); + if (titleStr) { + fputs(titleStr->c_str(), output); + } if (linkName) { fputs("", output); delete linkName; } - delete titleStr; atLeastOne = true; item->open(); @@ -1778,14 +1767,14 @@ void HtmlOutputDev::newXmlOutlineLevel(FILE *output, const std::vector\n", output); for (OutlineItem *item : *outlines) { - GooString *titleStr = HtmlFont::HtmlFilter(item->getTitle(), item->getTitleLength()); + const std::vector &title = item->getTitle(); + auto titleStr = HtmlFont::HtmlFilter(title.data(), title.size()); const int itemPage = getOutlinePageNum(item); if (itemPage > 0) { fprintf(output, "%s\n", itemPage, titleStr->c_str()); } else { fprintf(output, "%s\n", titleStr->c_str()); } - delete titleStr; item->open(); if (item->hasKids() && item->getKids()) { diff --git a/utils/HtmlOutputDev.h b/utils/HtmlOutputDev.h index c7b08d1..4bff464 100644 --- a/utils/HtmlOutputDev.h +++ b/utils/HtmlOutputDev.h @@ -14,7 +14,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2006, 2007, 2009, 2012, 2018-2022 Albert Astals Cid +// Copyright (C) 2006, 2007, 2009, 2012, 2018-2022. 2024 Albert Astals Cid // Copyright (C) 2008, 2009 Warren Toomey // Copyright (C) 2009, 2011 Carlos Garcia Campos // Copyright (C) 2009 Kovid Goyal @@ -25,7 +25,8 @@ // Copyright (C) 2012 Fabio D'Urso // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2024 Oliver Sander +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -45,7 +46,7 @@ #include "Catalog.h" #include "UnicodeMap.h" -#define xoutRound(x) ((int)(x + 0.5)) +#define xoutRound(x) ((int)((x) + 0.5)) #define DOCTYPE "" @@ -95,7 +96,7 @@ class HtmlString HtmlString *yxNext; // next string in y-major order HtmlString *xyNext; // next string in x-major order int fontpos; - GooString *htext; + std::unique_ptr htext; int len; // length of text and xRight int size; // size of text and xRight arrays UnicodeTextDirection dir; // direction (left to right/right to left) @@ -144,7 +145,7 @@ class HtmlPage void AddLink(const HtmlLink &x) { links->AddLink(x); } // add an image to the current page - void addImage(std::unique_ptr &&fname, GfxState *state); + void addImage(std::string &&fname, GfxState *state); // number of images on the current page int getNumImages() { return imgList.size(); } @@ -286,7 +287,7 @@ class HtmlOutputDev : public OutputDev // recognized. static std::string mapEncodingToHtml(const std::string &encoding); void doProcessLink(AnnotLink *link); - GooString *getLinkDest(AnnotLink *link); + std::unique_ptr getLinkDest(AnnotLink *link); void dumpMetaVars(FILE *); void doFrame(int firstPage); bool newHtmlOutlineLevel(FILE *output, const std::vector *outlines, int level = 1); @@ -294,7 +295,7 @@ class HtmlOutputDev : public OutputDev int getOutlinePageNum(OutlineItem *item); void drawJpegImage(GfxState *state, Stream *str); void drawPngImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool isMask = false); - std::unique_ptr createImageFileName(const char *ext); + std::string createImageFileName(const char *ext); FILE *fContentsFrame; FILE *page; // html file diff --git a/utils/ImageOutputDev.cc b/utils/ImageOutputDev.cc index 6c6ec16..c53afba 100644 --- a/utils/ImageOutputDev.cc +++ b/utils/ImageOutputDev.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2005, 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2005, 2007, 2011, 2018, 2019, 2021, 2022, 2025 Albert Astals Cid // Copyright (C) 2006 Rainer Keller // Copyright (C) 2008 Timothy Lee // Copyright (C) 2008 Vasile Gaburici @@ -26,6 +26,9 @@ // Copyright (C) 2017 Caolán McNamara // Copyright (C) 2018 Andreas Gruenbacher // Copyright (C) 2020 mrbax <12640-mrbax@users.noreply.gitlab.freedesktop.org> +// Copyright (C) 2024 Fernando Herrera +// Copyright (C) 2024 Sebastian J. Bronner +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -65,9 +68,10 @@ ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImages dumpJBIG2 = false; dumpCCITT = false; pageNames = pageNamesA; + printFilenames = false; imgNum = 0; pageNum = 0; - ok = true; + errorCode = 0; if (listImages) { printf("page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n"); printf("--------------------------------------------------------------------------------------------\n"); @@ -300,16 +304,20 @@ long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, Gf long len; if (colorMap) { - ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + ImageStream imgStr(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); + if (!imgStr.reset()) { + imgStr.close(); + return 0; + } for (int y = 0; y < height; y++) { - imgStr->getLine(); + imgStr.getLine(); } - imgStr->close(); - delete imgStr; + imgStr.close(); } else { - str->reset(); + if (!str->reset()) { + return 0; + } for (int y = 0; y < height; y++) { int size = (width + 7) / 8; for (int x = 0; x < size; x++) { @@ -340,12 +348,18 @@ void ImageOutputDev::writeRawImage(Stream *str, const char *ext) ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + errorCode = 2; return; } // initialize stream str = str->getNextStream(); - str->reset(); + if (!str->reset()) { + fclose(f); + error(errIO, -1, "Couldn't reset stream"); + errorCode = 2; + return; + } // copy the stream while ((c = str->getChar()) != EOF) { @@ -360,7 +374,6 @@ void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const { FILE *f = nullptr; /* squelch bogus compiler warning */ ImageStream *imgStr = nullptr; - unsigned char *row; unsigned char *rowp; unsigned char *p; GfxRGB rgb; @@ -374,11 +387,13 @@ void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const ++imgNum; if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + errorCode = 2; return; } if (!writer->init(f, width, height, 72, 72)) { error(errIO, -1, "Error writing '{0:s}'", fileName); + errorCode = 2; return; } } @@ -388,19 +403,28 @@ void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const pixelSize = 2 * sizeof(unsigned int); } - row = (unsigned char *)gmallocn_checkoverflow(width, pixelSize); - if (!row) { - error(errIO, -1, "Image data for '{0:s}' is too big. {1:d} width with {2:d} bytes per pixel", fileName, width, pixelSize); - return; - } - if (format != imgMonochrome) { // initialize stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); + if (!imgStr->reset()) { + error(errIO, -1, "Stream reset failed"); + errorCode = 3; + return; + } } else { // initialize stream - str->reset(); + if (!str->reset()) { + errorCode = 3; + error(errIO, -1, "Stream reset failed"); + return; + } + } + + unsigned char *row = (unsigned char *)gmallocn_checkoverflow(width, pixelSize); + if (!row) { + error(errIO, -1, "Image data for '{0:s}' is too big. {1:d} width with {2:d} bytes per pixel", fileName, width, pixelSize); + errorCode = 99; + return; } // PDF masks use 0 = draw current color, 1 = leave unchanged. @@ -559,13 +583,15 @@ void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int w setFilename("jb2g"); if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + errorCode = 2; return; } - globalsStr->reset(); - while ((c = globalsStr->getChar()) != EOF) { - fputc(c, f); + if (globalsStr->reset()) { + while ((c = globalsStr->getChar()) != EOF) { + fputc(c, f); + } + globalsStr->close(); } - globalsStr->close(); fclose(f); } @@ -579,6 +605,7 @@ void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int w setFilename("params"); if (!(f = fopen(fileName, "wb"))) { error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); + errorCode = 2; return; } if (ccittStr->getEncoding() < 0) { @@ -683,6 +710,10 @@ void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int w if (inlineImg) { embedStr->restore(); } + + if (printFilenames) { + printf("%s\n", fileName); + } } bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep) diff --git a/utils/ImageOutputDev.h b/utils/ImageOutputDev.h index 67b77b7..d0f6d30 100644 --- a/utils/ImageOutputDev.h +++ b/utils/ImageOutputDev.h @@ -19,7 +19,9 @@ // Copyright (C) 2010 Jakob Voss // Copyright (C) 2012, 2013, 2017 Adrian Johnson // Copyright (C) 2013 Thomas Freitag -// Copyright (C) 2018, 2019, 2021 Albert Astals Cid +// Copyright (C) 2018, 2019, 2021, 2024 Albert Astals Cid +// Copyright (C) 2024 Fernando Herrera +// Copyright (C) 2024 Sebastian J. Bronner // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -89,8 +91,15 @@ class ImageOutputDev : public OutputDev // Use CCITT format for CCITT files void enableCCITT(bool ccitt) { dumpCCITT = ccitt; } + // Print filenames to stdout after writing + void enablePrintFilenames(bool filenames) { printFilenames = filenames; } + + // Get the error code + // 0 = No error, 1 = Error opening a PDF file, 2 = Error opening an output file, 3 = Error related to PDF permissions, 99 = Other error. + int getErrorCode() const { return errorCode; } + // Check if file was successfully created. - virtual bool isOk() { return ok; } + virtual bool isOk() { return errorCode == 0; } // Does this device use tilingPatternFill()? If this returns false, // tiling pattern fills will be reduced to a series of other drawing @@ -145,9 +154,10 @@ class ImageOutputDev : public OutputDev bool outputPNG; // set to output in PNG format bool outputTiff; // set to output in TIFF format bool pageNames; // set to include page number in file names + bool printFilenames; // set to print image filenames to stdout after writing int pageNum; // current page number int imgNum; // current image number - bool ok; // set up ok? + int errorCode; // code for any error creating the output files }; #endif diff --git a/utils/InMemoryFile.h b/utils/InMemoryFile.h index f4fcb27..4690f1a 100644 --- a/utils/InMemoryFile.h +++ b/utils/InMemoryFile.h @@ -45,15 +45,11 @@ class InMemoryFile public: InMemoryFile(); -public: /* Returns a file handle for this file. This is scoped to this object * and must be fclosed() by the caller before destruction. */ FILE *open(const char *mode); - const std::vector &getBuffer() const - { - return data; - } + const std::vector &getBuffer() const { return data; } }; #endif // IN_MEMORY_FILE_H diff --git a/utils/Win32Console.cc b/utils/Win32Console.cc index ea46e17..57e825a 100644 --- a/utils/Win32Console.cc +++ b/utils/Win32Console.cc @@ -4,7 +4,9 @@ // // This file is licensed under the GPLv2 or later // -// Copyright (C) 2017 Adrian Johnson +// Copyright (C) 2017, 2024 Adrian Johnson +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela +// Copyright (C) 2025 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -25,7 +27,6 @@ static const int BUF_SIZE = 4096; static int bufLen = 0; static char buf[BUF_SIZE]; -static wchar_t wbuf[BUF_SIZE]; static bool stdoutIsConsole = true; static bool stderrIsConsole = true; static HANDLE consoleHandle = nullptr; @@ -36,7 +37,7 @@ static HANDLE consoleHandle = nullptr; // writes. static void flush(bool all = false) { - int nchars = 0; + unsigned int nchars = 0; if (all || bufLen > BUF_SIZE / 2) { nchars = bufLen; @@ -49,8 +50,8 @@ static void flush(bool all = false) } if (nchars > 0) { - DWORD wlen = utf8ToUtf16(buf, (uint16_t *)wbuf, BUF_SIZE, nchars); - WriteConsoleW(consoleHandle, wbuf, wlen, &wlen, nullptr); + std::u16string u16string = utf8ToUtf16(std::string_view { buf, nchars }); + WriteConsoleW(consoleHandle, u16string.data(), u16string.size(), nullptr, nullptr); if (nchars < bufLen) { memmove(buf, buf + nchars, bufLen - nchars); bufLen -= nchars; @@ -120,7 +121,8 @@ Win32Console::Win32Console(int *argc, char **argv[]) argList = new char *[numArgs]; privateArgList = new char *[numArgs]; for (int i = 0; i < numArgs; i++) { - argList[i] = utf16ToUtf8((uint16_t *)(wargv[i])); + std::string arg = utf16ToUtf8((uint16_t *)(wargv[i])); + argList[i] = strdup(arg.c_str()); // parseArgs will rearrange the argv list so we keep our own copy // to use for freeing all the strings privateArgList[i] = argList[i]; @@ -132,7 +134,6 @@ Win32Console::Win32Console(int *argc, char **argv[]) bufLen = 0; buf[0] = 0; - wbuf[0] = 0; // check if stdout or stderr redirected // GetFileType() returns CHAR for console and special devices COMx, PRN, CON, NUL etc @@ -155,7 +156,7 @@ Win32Console::~Win32Console() flush(true); if (argList) { for (int i = 0; i < numArgs; i++) - gfree(privateArgList[i]); + free(privateArgList[i]); delete[] argList; delete[] privateArgList; } diff --git a/utils/pdfattach.cc b/utils/pdfattach.cc index 96d6f0f..414b006 100644 --- a/utils/pdfattach.cc +++ b/utils/pdfattach.cc @@ -5,7 +5,8 @@ // This file is licensed under the GPLv2 or later // // Copyright (C) 2019-2022 Albert Astals Cid -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2023 Oliver Sander +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -14,7 +15,7 @@ #include "config.h" #include -#include "gbasename.h" +#include "goo/gbasename.h" #include "parseargs.h" #include "GlobalParams.h" #include "PDFDoc.h" @@ -86,14 +87,14 @@ int main(int argc, char *argv[]) return 3; } - const std::unique_ptr attachFileName = utf8ToUtf16WithBom(gbasename(attachFilePath.c_str())); + const std::string attachFileName = utf8ToUtf16WithBom(gbasename(attachFilePath.c_str())); - if (!doReplace && doc->getCatalog()->hasEmbeddedFile(attachFileName->toStr())) { - fprintf(stderr, "There is already an embedded file named %s.\n", attachFileName->c_str()); + if (!doReplace && doc->getCatalog()->hasEmbeddedFile(attachFileName)) { + fprintf(stderr, "There is already an embedded file named %s.\n", attachFileName.c_str()); return 4; } - doc->getCatalog()->addEmbeddedFile(attachFile.get(), attachFileName->toStr()); + doc->getCatalog()->addEmbeddedFile(attachFile.get(), attachFileName); const GooString outputPdfFilePath(argv[3]); const int saveResult = doc->saveAs(outputPdfFilePath); diff --git a/utils/pdfdetach.cc b/utils/pdfdetach.cc index 247c2c1..d370494 100644 --- a/utils/pdfdetach.cc +++ b/utils/pdfdetach.cc @@ -16,10 +16,11 @@ // Copyright (C) 2011 Carlos Garcia Campos // Copyright (C) 2013 Yury G. Kudryashov // Copyright (C) 2014, 2017 Adrian Johnson -// Copyright (C) 2018, 2020, 2022 Albert Astals Cid +// Copyright (C) 2018, 2020, 2022, 2024 Albert Astals Cid // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019, 2021 Oliver Sander +// Copyright (C) 2019, 2021, 2024 Oliver Sander // Copyright (C) 2020 +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -42,8 +43,11 @@ #include "UnicodeMap.h" #include "PDFDocEncoding.h" #include "Error.h" +#include "UTF.h" #include "Win32Console.h" +#include + static bool doList = false; static int saveNum = 0; static char saveFile[128] = ""; @@ -70,24 +74,46 @@ static const ArgDesc argDesc[] = { { "-list", argFlag, &doList, 0, "list all emb { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; +static std::string getFileName(const GooString &s, const UnicodeMap &uMap) +{ + int j; + Unicode u; + bool isUnicode; + char uBuf[8]; + std::string res; + if (hasUnicodeByteOrderMarkAndLengthIsEven(s.toStr())) { + isUnicode = true; + j = 2; + } else { + isUnicode = false; + j = 0; + } + while (j < s.getLength()) { + if (isUnicode) { + u = ((s.getChar(j) & 0xff) << 8) | (s.getChar(j + 1) & 0xff); + j += 2; + } else { + u = pdfDocEncoding[s.getChar(j) & 0xff]; + ++j; + } + const int n = uMap.mapUnicode(u, uBuf, sizeof(uBuf)); + res.append(uBuf, n); + } + return res; +} + int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName; const UnicodeMap *uMap; std::optional ownerPW, userPW; - char uBuf[8]; - char path[1024]; - char *p; bool ok; bool hasSaveFile; std::vector> embeddedFiles; - int nFiles, nPages, n, i, j; + int nFiles, nPages, i; Page *page; Annots *annots; - const GooString *s1; - Unicode u; - bool isUnicode; Win32Console win32Console(&argc, &argv); @@ -166,80 +192,46 @@ int main(int argc, char *argv[]) for (i = 0; i < nFiles; ++i) { const std::unique_ptr &fileSpec = embeddedFiles[i]; printf("%d: ", i + 1); - s1 = fileSpec->getFileName(); + const GooString *s1 = fileSpec->getFileName(); if (!s1) { return 3; } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; - } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); - j += 2; - } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; - } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - fwrite(uBuf, 1, n, stdout); - } - fputc('\n', stdout); + printf("%s\n", getFileName(*s1, *uMap).c_str()); } // save all embedded files } else if (saveAll) { + std::filesystem::path basePath = savePath; + if (basePath.empty()) { + basePath = std::filesystem::current_path(); + } + basePath = basePath.lexically_normal(); + for (i = 0; i < nFiles; ++i) { const std::unique_ptr &fileSpec = embeddedFiles[i]; - if (savePath[0]) { - n = strlen(savePath); - if (n > (int)sizeof(path) - 2) { - n = sizeof(path) - 2; - } - memcpy(path, savePath, n); - path[n] = '/'; - p = path + n + 1; - } else { - p = path; - } - s1 = fileSpec->getFileName(); + + const GooString *s1 = fileSpec->getFileName(); if (!s1) { return 3; } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; + const std::string currentFileName = getFileName(*s1, *uMap); + if (currentFileName.empty()) { + return 3; } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); - j += 2; - } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; - } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) { - break; - } - memcpy(p, uBuf, n); - p += n; + std::filesystem::path filePath = basePath; + filePath = filePath.append(currentFileName).lexically_normal(); + + if (!filePath.generic_string().starts_with(basePath.generic_string())) { + error(errIO, -1, "Preventing directory traversal"); + return 3; } - *p = '\0'; auto *embFile = fileSpec->getEmbeddedFile(); if (!embFile || !embFile->isOk()) { return 3; } - if (!embFile->save(path)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + if (!embFile->save(filePath.generic_string())) { + error(errIO, -1, "Error saving embedded file as '{0:s}'", filePath.c_str()); return 2; } } @@ -249,8 +241,12 @@ int main(int argc, char *argv[]) if (hasSaveFile) { for (i = 0; i < nFiles; ++i) { const std::unique_ptr &fileSpec = embeddedFiles[i]; - s1 = fileSpec->getFileName(); - if (strcmp(s1->c_str(), saveFile) == 0) { + const GooString *s1 = fileSpec->getFileName(); + if (!s1) { + continue; + } + const std::string currentFileName = getFileName(*s1, *uMap); + if (currentFileName == saveFile) { saveNum = i + 1; break; } @@ -262,46 +258,33 @@ int main(int argc, char *argv[]) } const std::unique_ptr &fileSpec = embeddedFiles[saveNum - 1]; - if (savePath[0]) { - p = savePath; - } else { - p = path; - s1 = fileSpec->getFileName(); + std::string targetPath = savePath; + if (targetPath.empty()) { + // The user hasn't given a path to save, just use the filename specified in the pdf as name + const GooString *s1 = fileSpec->getFileName(); if (!s1) { return 3; } - if (s1->hasUnicodeMarker()) { - isUnicode = true; - j = 2; - } else { - isUnicode = false; - j = 0; - } - while (j < s1->getLength()) { - if (isUnicode) { - u = ((s1->getChar(j) & 0xff) << 8) | (s1->getChar(j + 1) & 0xff); - j += 2; - } else { - u = pdfDocEncoding[s1->getChar(j) & 0xff]; - ++j; - } - n = uMap->mapUnicode(u, uBuf, sizeof(uBuf)); - if (p + n >= path + sizeof(path)) { - break; - } - memcpy(p, uBuf, n); - p += n; + const std::string targetFileName = getFileName(*s1, *uMap); + targetPath.append(targetFileName); + + const std::filesystem::path basePath = std::filesystem::current_path().lexically_normal(); + std::filesystem::path filePath = basePath; + filePath = filePath.append(targetPath).lexically_normal(); + + if (!filePath.generic_string().starts_with(basePath.generic_string())) { + error(errIO, -1, "Preventing directory traversal"); + return 3; } - *p = '\0'; - p = path; + targetPath = filePath.generic_string(); } auto *embFile = fileSpec->getEmbeddedFile(); if (!embFile || !embFile->isOk()) { return 3; } - if (!embFile->save(p)) { - error(errIO, -1, "Error saving embedded file as '{0:s}'", p); + if (!embFile->save(targetPath)) { + error(errIO, -1, "Error saving embedded file as '{0:s}'", targetPath.c_str()); return 2; } } diff --git a/utils/pdffonts.cc b/utils/pdffonts.cc index ed45043..14a30fc 100644 --- a/utils/pdffonts.cc +++ b/utils/pdffonts.cc @@ -14,7 +14,7 @@ // under GPL version 2 or later // // Copyright (C) 2006 Dominic Lachowicz -// Copyright (C) 2007-2008, 2010, 2018, 2022 Albert Astals Cid +// Copyright (C) 2007-2008, 2010, 2018, 2022, 2024, 2025 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2012, 2017 Adrian Johnson // Copyright (C) 2013 Suzuki Toshiya @@ -132,15 +132,18 @@ int main(int argc, char *argv[]) printf("name object ID substitute font substitute font file\n"); printf("------------------------------------ --------- ------------------------------------ ------------------------------------\n"); for (const FontInfo *font : fonts) { - if (font->getFile()) { - printf("%-36s", font->getName() ? font->getName()->c_str() : "[none]"); + const std::optional &fontFile = font->getFile(); + if (fontFile) { + const std::optional &fontName = font->getName(); + printf("%-36s", fontName ? fontName->c_str() : "[none]"); const Ref fontRef = font->getRef(); if (fontRef.gen >= 100000) { printf(" [none]"); } else { printf(" %6d %2d", fontRef.num, fontRef.gen); } - printf(" %-36s %s\n", font->getSubstituteName() ? font->getSubstituteName()->c_str() : "[none]", font->getFile()->c_str()); + const std::optional &fontSubstituteName = font->getSubstituteName(); + printf(" %-36s %s\n", fontSubstituteName ? fontSubstituteName->c_str() : "[none]", fontFile->c_str()); } delete font; } @@ -149,8 +152,9 @@ int main(int argc, char *argv[]) printf("name type encoding emb sub uni object ID\n"); printf("------------------------------------ ----------------- ---------------- --- --- --- ---------\n"); for (const FontInfo *font : fonts) { - printf("%-36s %-17s %-16s %-3s %-3s %-3s", font->getName() ? font->getName()->c_str() : "[none]", fontTypeNames[font->getType()], font->getEncoding().c_str(), font->getEmbedded() ? "yes" : "no", - font->getSubset() ? "yes" : "no", font->getToUnicode() ? "yes" : "no"); + const std::optional &fontName = font->getName(); + printf("%-36s %-17s %-16s %-3s %-3s %-3s", fontName ? fontName->c_str() : "[none]", fontTypeNames[font->getType()], font->getEncoding().c_str(), font->getEmbedded() ? "yes" : "no", font->getSubset() ? "yes" : "no", + font->getToUnicode() ? "yes" : "no"); const Ref fontRef = font->getRef(); if (fontRef.gen >= 100000) { printf(" [none]\n"); diff --git a/utils/pdfimages.1 b/utils/pdfimages.1 index 8afa9b7..1a5386a 100644 --- a/utils/pdfimages.1 +++ b/utils/pdfimages.1 @@ -219,6 +219,9 @@ Specify the user password for the PDF file. .B \-p Include page numbers in output file names. .TP +.B \-print\-filenames +Print image filenames to stdout. +.TP .B \-q Don't print any messages or errors. .TP diff --git a/utils/pdfimages.cc b/utils/pdfimages.cc index 42697e1..f208377 100644 --- a/utils/pdfimages.cc +++ b/utils/pdfimages.cc @@ -15,7 +15,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2007-2008, 2010, 2018, 2022 Albert Astals Cid +// Copyright (C) 2007-2008, 2010, 2018, 2022, 2024 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 Jakob Voss // Copyright (C) 2012, 2013, 2017 Adrian Johnson @@ -23,6 +23,8 @@ // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019, 2021 Oliver Sander // Copyright (C) 2019 Hartmut Goebel +// Copyright (C) 2024 Fernando Herrera +// Copyright (C) 2024 Sebastian J. Bronner // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -63,6 +65,7 @@ static bool dumpJBIG2 = false; static bool dumpCCITT = false; static bool allFormats = false; static bool pageNames = false; +static bool printFilenames = false; static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool quiet = false; @@ -86,6 +89,7 @@ static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-p", argFlag, &pageNames, 0, "include page numbers in output file names" }, + { "-print-filenames", argFlag, &printFilenames, 0, "print image filenames to stdout" }, { "-q", argFlag, &quiet, 0, "don't print any messages or errors" }, { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, { "-h", argFlag, &printHelp, 0, "print usage information" }, @@ -96,16 +100,13 @@ static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to int main(int argc, char *argv[]) { - GooString *fileName; char *imgRoot = nullptr; std::optional ownerPW, userPW; - ImageOutputDev *imgOut; - bool ok; Win32Console win32Console(&argc, &argv); // parse args - ok = parseArgs(argDesc, &argc, argv); + const bool ok = parseArgs(argDesc, &argc, argv); if (!ok || (listImages && argc != 2) || (!listImages && argc != 3) || printVersion || printHelp) { fprintf(stderr, "pdfimages version %s\n", PACKAGE_VERSION); fprintf(stderr, "%s\n", popplerCopyright); @@ -118,7 +119,7 @@ int main(int argc, char *argv[]) } return 99; } - fileName = new GooString(argv[1]); + GooString *fileName = new GooString(argv[1]); if (!listImages) { imgRoot = argv[2]; } @@ -173,7 +174,7 @@ int main(int argc, char *argv[]) } // write image files - imgOut = new ImageOutputDev(imgRoot, pageNames, listImages); + ImageOutputDev *imgOut = new ImageOutputDev(imgRoot, pageNames, listImages); if (imgOut->isOk()) { if (allFormats) { imgOut->enablePNG(true); @@ -190,9 +191,10 @@ int main(int argc, char *argv[]) imgOut->enableJBig2(dumpJBIG2); imgOut->enableCCITT(dumpCCITT); } + imgOut->enablePrintFilenames(printFilenames); doc->displayPages(imgOut, firstPage, lastPage, 72, 72, 0, true, false, false); } + const int exitCode = imgOut->isOk() ? 0 : imgOut->getErrorCode(); delete imgOut; - - return 0; + return exitCode; } diff --git a/utils/pdfinfo.cc b/utils/pdfinfo.cc index 1f4ca79..440f317 100644 --- a/utils/pdfinfo.cc +++ b/utils/pdfinfo.cc @@ -15,7 +15,7 @@ // under GPL version 2 or later // // Copyright (C) 2006 Dom Lachowicz -// Copyright (C) 2007-2010, 2012, 2016-2022 Albert Astals Cid +// Copyright (C) 2007-2010, 2012, 2016-2022, 2024 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2011 Vittal Aithal // Copyright (C) 2012, 2013, 2016-2018, 2021 Adrian Johnson @@ -28,6 +28,7 @@ // Copyright (C) 2019 Christian Persch // Copyright (C) 2019-2021 Oliver Sander // Copyright (C) 2019 Thomas Fischer +// Copyright (C) 2024, 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -43,6 +44,7 @@ #include #include #include +#include #include #include "parseargs.h" #include "printencodings.h" @@ -112,23 +114,26 @@ static const ArgDesc argDesc[] = { { "-f", argInt, &firstPage, 0, "first page to { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; -static void printTextString(const GooString *s, const UnicodeMap *uMap) +static void printStdTextString(const std::string &s, const UnicodeMap *uMap) { - Unicode *u; char buf[8]; - int len = TextStringToUCS4(s->toStr(), &u); - for (int i = 0; i < len; i++) { - int n = uMap->mapUnicode(u[i], buf, sizeof(buf)); + const std::vector u = TextStringToUCS4(s); + for (const auto &c : u) { + int n = uMap->mapUnicode(c, buf, sizeof(buf)); fwrite(buf, 1, n, stdout); } - gfree(u); } -static void printUCS4String(const Unicode *u, int len, const UnicodeMap *uMap) +static void printTextString(const GooString *s, const UnicodeMap *uMap) +{ + printStdTextString(s->toStr(), uMap); +} + +static void printUCS4String(const std::vector &u, const UnicodeMap *uMap) { char buf[8]; - for (int i = 0; i < len; i++) { - int n = uMap->mapUnicode(u[i], buf, sizeof(buf)); + for (auto i : u) { + int n = uMap->mapUnicode(i, buf, sizeof(buf)); fwrite(buf, 1, n, stdout); } } @@ -234,8 +239,8 @@ static void printAttribute(const Attribute *attribute, unsigned indent) printIndent(indent); printf(" /%s ", attribute->getTypeName()); if (attribute->getType() == Attribute::UserProperty) { - std::unique_ptr name = attribute->getName(); - printf("(%s) ", name->c_str()); + const char *name = attribute->getName(); + printf("(%s) ", name); } attribute->getValue()->print(stdout); if (attribute->getFormattedValue()) { @@ -295,11 +300,6 @@ static void printStruct(const StructElement *element, unsigned indent) } } -struct GooStringCompare -{ - bool operator()(GooString *lhs, GooString *rhs) const { return lhs->cmp(const_cast(rhs)) < 0; } -}; - static void printLinkDest(const std::unique_ptr &dest) { GooString s; @@ -370,29 +370,25 @@ static void printLinkDest(const std::unique_ptr &dest) static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) { - std::map, GooStringCompare>> map; + std::map>> map; int numDests = doc->getCatalog()->numDestNameTree(); for (int i = 0; i < numDests; i++) { - GooString *name = new GooString(doc->getCatalog()->getDestNameTreeName(i)); + const GooString *name = doc->getCatalog()->getDestNameTreeName(i); std::unique_ptr dest = doc->getCatalog()->getDestNameTreeDest(i); - if (dest && dest->isPageRef()) { + if (name && dest && dest->isPageRef()) { Ref pageRef = dest->getPageRef(); - map[pageRef].insert(std::make_pair(name, std::move(dest))); - } else { - delete name; + map[pageRef].insert(std::make_pair(name->toStr(), std::move(dest))); } } numDests = doc->getCatalog()->numDests(); for (int i = 0; i < numDests; i++) { - GooString *name = new GooString(doc->getCatalog()->getDestsName(i)); + const char *name = doc->getCatalog()->getDestsName(i); std::unique_ptr dest = doc->getCatalog()->getDestsDest(i); - if (dest && dest->isPageRef()) { + if (name && dest && dest->isPageRef()) { Ref pageRef = dest->getPageRef(); map[pageRef].insert(std::make_pair(name, std::move(dest))); - } else { - delete name; } } @@ -406,9 +402,8 @@ static void printDestinations(PDFDoc *doc, const UnicodeMap *uMap) printf("%4d ", i); printLinkDest(it.second); printf(" \""); - printTextString(it.first, uMap); + printStdTextString(it.first, uMap); printf("\"\n"); - delete it.first; } } } @@ -424,7 +419,7 @@ static void printUrlList(PDFDoc *doc) std::unique_ptr links = page->getLinks(); for (AnnotLink *annot : links->getLinks()) { LinkAction *action = annot->getAction(); - if (action->getKind() == actionURI) { + if (action && action->getKind() == actionURI) { LinkURI *linkUri = dynamic_cast(action); std::string uri = linkUri->getURI(); printf("%4d Annotation %s\n", pg, uri.c_str()); @@ -658,13 +653,13 @@ static void printPdfSubtype(PDFDoc *doc, const UnicodeMap *uMap) printf(" Title: %s\n", typeExp->c_str()); printf(" Abbreviation: %s\n", abbr->c_str()); - if (part.get()) { + if (part) { printf(" Subtitle: Part %d: %s\n", subpart, part->c_str()); } else { printf(" Subtitle: Part %d\n", subpart); } printf(" Standard: %s-%d\n", typeExp->toStr().substr(0, 9).c_str(), subpart); - if (confExp.get()) { + if (confExp) { printf(" Conformance: %s\n", confExp->c_str()); } } @@ -706,15 +701,12 @@ static void printCustomInfo(PDFDoc *doc, const UnicodeMap *uMap) Object obj = dict->lookup(key.c_str()); if (obj.isString()) { // print key - Unicode *u; - int len = utf8ToUCS4(key.c_str(), &u); - printUCS4String(u, len, uMap); + std::vector u = utf8ToUCS4(key); + printUCS4String(u, uMap); fputs(":", stdout); - while (len < 16) { + for (size_t i = u.size(); i < 16; i++) { fputs(" ", stdout); - len++; } - gfree(u); // print value GooString val_str(obj.getString()); @@ -848,8 +840,8 @@ static void printInfo(PDFDoc *doc, const UnicodeMap *uMap, long long filesize, b if ((fabs(w - 612) < 1 && fabs(h - 792) < 1) || (fabs(w - 792) < 1 && fabs(h - 612) < 1)) { printf(" (letter)"); } else { - hISO = sqrt(sqrt(2.0)) * 7200 / 2.54; - wISO = hISO / sqrt(2.0); + hISO = sqrt(std::numbers::sqrt2) * 7200 / 2.54; + wISO = hISO / std::numbers::sqrt2; isoThreshold = hISO * 0.003; ///< allow for 0.3% error when guessing conformance to ISO 216, A series for (i = 0; i <= 6; ++i) { if ((fabs(w - wISO) < isoThreshold && fabs(h - hISO) < isoThreshold) || (fabs(w - hISO) < isoThreshold && fabs(h - wISO) < isoThreshold)) { @@ -857,8 +849,8 @@ static void printInfo(PDFDoc *doc, const UnicodeMap *uMap, long long filesize, b break; } hISO = wISO; - wISO /= sqrt(2.0); - isoThreshold /= sqrt(2.0); + wISO /= std::numbers::sqrt2; + isoThreshold /= std::numbers::sqrt2; } } printf("\n"); diff --git a/utils/pdfsig.1 b/utils/pdfsig.1 index 872c6e8..fa3bff5 100644 --- a/utils/pdfsig.1 +++ b/utils/pdfsig.1 @@ -17,17 +17,19 @@ the type of the signature as stated in the PDF and the signed ranges with a statement wether the total document is signed. It can also sign PDF documents (options -add-signature or -sign). .PP -pdfsig uses the trusted certificates stored in the Network Security Services (NSS) Database. +pdfsig uses the trusted certificates stored either in the Network Security Services (NSS) Database or in GnuPG's S/MIME system (gpgsm). .PP pdfsig also uses the Online Certificate Status Protocol (OCSP) (refer to http://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol) to look up the certificate online and check if it has been revoked (unless -no-ocsp has been specified). .PP -The NSS Database is searched for in the following locations: +If the NSS backend is used, the NSS Database is searched for in the following locations: .IP \(bu If the \-nssdir option is specified, the directory specified by this option. .IP \(bu The NSS Certificate database in the default Firefox profile. i.e. $HOME/.mozilla/firefox/*.default. .IP \(bu The NSS Certificate database in /etc/pki/nssdb. +.PP +If the GPG backend is used, the S/MIME certificate is read from $GNUPGHOME, defaulting to $HOME/.gnupg .SH OPTIONS .TP .B \-nssdir "[prefix]directory" @@ -46,11 +48,14 @@ Do not validate the certificate. .B \-no-ocsp Do not perform online OCSP certificate revocation check (local Certificate Revocation Lists (CRL) are still used). .TP +.B \-no-appearance +Do not add appearance information when signing existing fields (signer name and date). +.TP .B \-aia Enable the use of Authority Information Access (AIA) extension to fetch missing certificates to build the certificate chain. .TP .B \-dump -Dump all signatures into current directory. +Dump all signatures into current directory in their native format. Most likely it is either a unpadded or zero-padded CMS/PKCS7 bundle. .TP .B \-add-signature Add a new signature to the document. @@ -62,7 +67,10 @@ Specifies the field name to be used when adding a new signature. A random ID wil Sign the document in the specified signature field present in the document (must be unsigned). Field can be specified by field name (string) or the n-th signature field in the document (integer). .TP .B \-nick " nickname" -Use the certificate with the given nickname for signing. +Use the certificate with the given nickname for signing (NSS backend). If nickname starts with pkcs11:, it's treated as PKCS#11 URI (NSS backend). If the nickname is given as a fingerprint, it will be the certificate used (GPG backend) +.TP +.B \-backend " backend" +Use the specified backeng for cryptographic signatures .TP .B \-kpw " password" Use the given password for the signing key @@ -80,6 +88,9 @@ Create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached. .B \-list-nicks List available nicknames in the NSS database. .TP +.B \-list-backends +List available backends for cryptographic signatures +.TP .B \-v Print copyright and version information. .TP @@ -97,6 +108,9 @@ Displays signature info for signed_file.pdf. pdfsig input.pdf output.pdf -add-signature -nss-pwd password -nick my-cert -reason 'for fun!' Creates a new pdf named output.pdf with the contents of input.pdf signed by the 'my-cert' certificate. .TP +pdfsig input.pdf output.pdf -add-signature -nss-pwd password -nick 'pkcs11:token=smartcard0;object=Second%20certificate;type=cert' +Same, but uses a PKCS#11 URI as defined in IETF RFC 7512 to select the certificate to be used for signing. +.TP pdfsig input.pdf output.pdf -sign 0 -nss-pwd password -nick my-cert -reason 'for fun!' Creates a new pdf named output.pdf with the contents of input.pdf signed by the 'my-cert' certificate. input.pdf must have an already existing un-signed signature field. .SH AUTHOR diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc index 1e4301f..9356523 100644 --- a/utils/pdfsig.cc +++ b/utils/pdfsig.cc @@ -6,18 +6,19 @@ // // Copyright 2015 André Guerreiro // Copyright 2015 André Esser -// Copyright 2015, 2017-2022 Albert Astals Cid +// Copyright 2015, 2017-2024 Albert Astals Cid // Copyright 2016 Markus Kilås // Copyright 2017, 2019 Hans-Ulrich Jüttner // Copyright 2017, 2019 Adrian Johnson // Copyright 2018 Chinmoy Ranjan Pradhan // Copyright 2019 Alexey Pavlov -// Copyright 2019 Oliver Sander +// Copyright 2019. 2023, 2024 Oliver Sander // Copyright 2019 Nelson Efrain A. Cruz // Copyright 2021 Georgiy Sgibnev . Work sponsored by lab50.net. // Copyright 2021 Theofilos Intzoglou // Copyright 2022 Felix Jung -// Copyright 2022 Erich E. Hoover +// Copyright 2022, 2024 Erich E. Hoover +// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -28,7 +29,6 @@ #include #include #include -#include #include #include #include "parseargs.h" @@ -38,9 +38,13 @@ #include "Page.h" #include "PDFDoc.h" #include "PDFDocFactory.h" +#include "DateInfo.h" #include "Error.h" #include "GlobalParams.h" -#include "SignatureHandler.h" +#ifdef ENABLE_NSS3 +# include "NSSCryptoSignBackend.h" +#endif +#include "CryptoSignBackend.h" #include "SignatureInfo.h" #include "Win32Console.h" #include "numberofcharacters.h" @@ -49,6 +53,14 @@ # include #endif +#ifdef HAVE_GETTEXT +# include +# include +# define _(STRING) gettext(STRING) +#else +# define _(STRING) STRING +#endif + static const char *getReadableSigState(SignatureValidationStatus sig_vs) { switch (sig_vs) { @@ -107,8 +119,8 @@ static char *getReadableTime(time_t unix_time) static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, const char *filename) { - const GooString *signature = s->getSignature(); - if (!signature) { + const std::vector &signature = s->getSignature(); + if (signature.empty()) { printf("Cannot dump signature #%d\n", sig_num); return false; } @@ -117,11 +129,11 @@ static bool dumpSignature(int sig_num, int sigCount, FormFieldSignature *s, cons // We want format to be {0:s}.sig{1:Xd} where X is sigCountLength // since { is the magic character to replace things we need to put it twice where // we don't want it to be replaced - const std::unique_ptr format = GooString::format("{{0:s}}.sig{{1:{0:d}d}}", sigCountLength); - const std::unique_ptr path = GooString::format(format->c_str(), gbasename(filename).c_str(), sig_num); - printf("Signature #%d (%u bytes) => %s\n", sig_num, signature->getLength(), path->c_str()); - std::ofstream outfile(path->c_str(), std::ofstream::binary); - outfile.write(signature->c_str(), signature->getLength()); + const std::string format = GooString::format("{{0:s}}.sig{{1:{0:d}d}}", sigCountLength); + const std::string path = GooString::format(format.c_str(), gbasename(filename).c_str(), sig_num); + printf("Signature #%d (%lu bytes) => %s\n", sig_num, signature.size(), path.c_str()); + std::ofstream outfile(path.c_str(), std::ofstream::binary); + outfile.write(reinterpret_cast(signature.data()), signature.size()); outfile.close(); return true; @@ -133,10 +145,13 @@ static char ownerPassword[33] = "\001"; static char userPassword[33] = "\001"; static bool printVersion = false; static bool printHelp = false; +static bool printCryptoSignBackends = false; static bool dontVerifyCert = false; static bool noOCSPRevocationCheck = false; +static bool noAppearance = false; static bool dumpSignatures = false; static bool etsiCAdESdetached = false; +static char backendString[256] = ""; static char signatureName[256] = ""; static char certNickname[256] = ""; static char password[256] = ""; @@ -151,22 +166,26 @@ static const ArgDesc argDesc[] = { { "-nssdir", argGooString, &nssDir, 0, "path { "-nss-pwd", argGooString, &nssPassword, 0, "password to access the NSS database (if any)" }, { "-nocert", argFlag, &dontVerifyCert, 0, "don't perform certificate validation" }, { "-no-ocsp", argFlag, &noOCSPRevocationCheck, 0, "don't perform online OCSP certificate revocation check" }, + { "-no-appearance", argFlag, &noAppearance, 0, "don't add appearance information when signing existing fields" }, { "-aia", argFlag, &useAIACertFetch, 0, "use Authority Information Access (AIA) extension for certificate fetching" }, { "-dump", argFlag, &dumpSignatures, 0, "dump all signatures into current directory" }, { "-add-signature", argFlag, &addNewSignature, 0, "adds a new signature to the document" }, { "-new-signature-field-name", argGooString, &newSignatureFieldName, 0, "field name used for the newly added signature. A random ID will be used if empty" }, { "-sign", argString, &signatureName, 256, "sign the document in the given signature field (by name or number)" }, { "-etsi", argFlag, &etsiCAdESdetached, 0, "create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached" }, - { "-nick", argString, &certNickname, 256, "use the certificate with the given nickname for signing" }, + { "-backend", argString, &backendString, 256, "use given backend for signing/verification" }, + { "-nick", argString, &certNickname, 256, "use the certificate with the given nickname/fingerprint for signing" }, { "-kpw", argString, &password, 256, "password for the signing key (might be missing if the key isn't password protected)" }, { "-digest", argString, &digestName, 256, "name of the digest algorithm (default: SHA256)" }, { "-reason", argGooString, &reason, 0, "reason for signing (default: no reason given)" }, { "-list-nicks", argFlag, &listNicknames, 0, "list available nicknames in the NSS database" }, + { "-list-backends", argFlag, &printCryptoSignBackends, 0, "print cryptographic signature backends" }, { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, { "-v", argFlag, &printVersion, 0, "print copyright and version info" }, { "-h", argFlag, &printHelp, 0, "print usage information" }, { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, { "-?", argFlag, &printHelp, 0, "print usage information" }, {} }; @@ -180,8 +199,29 @@ static void print_version_usage(bool usage) } } +static void print_backends() +{ + fprintf(stderr, "pdfsig backends:\n"); + for (const auto &backend : CryptoSign::Factory::getAvailable()) { + switch (backend) { + case CryptoSign::Backend::Type::NSS3: + fprintf(stderr, "NSS"); + break; + case CryptoSign::Backend::Type::GPGME: + fprintf(stderr, "GPG"); + break; + } + if (backend == CryptoSign::Factory::getActive()) { + fprintf(stderr, " (active)\n"); + } else { + fprintf(stderr, "\n"); + } + } +} + static std::vector> getAvailableSigningCertificates(bool *error) { +#ifdef ENABLE_NSS3 bool wrongPassword = false; bool passwordNeeded = false; auto passwordCallback = [&passwordNeeded, &wrongPassword](const char *) -> char * { @@ -198,9 +238,17 @@ static std::vector> getAvailableSigningCert return nullptr; } }; - SignatureHandler::setNSSPasswordCallback(passwordCallback); - std::vector> vCerts = SignatureHandler::getAvailableSigningCertificates(); - SignatureHandler::setNSSPasswordCallback({}); + NSSSignatureConfiguration::setNSSPasswordCallback(passwordCallback); +#endif + auto backend = CryptoSign::Factory::createActive(); + if (!backend) { + *error = true; + printf("No backends for cryptographic signatures available"); + return {}; + } + std::vector> vCerts = backend->getAvailableSigningCertificates(); +#ifdef ENABLE_NSS3 + NSSSignatureConfiguration::setNSSPasswordCallback({}); if (passwordNeeded) { *error = true; printf("Password is needed to access the NSS database.\n"); @@ -214,24 +262,38 @@ static std::vector> getAvailableSigningCert return {}; } +#endif *error = false; return vCerts; } +static std::string locationToString(KeyLocation location) +{ + switch (location) { + case KeyLocation::Unknown: + return {}; + case KeyLocation::Other: + return "(Other)"; + case KeyLocation::Computer: + return "(Computer)"; + case KeyLocation::HardwareToken: + return "(Hardware Token)"; + } + return {}; +} + static std::string TextStringToUTF8(const std::string &str) { const UnicodeMap *utf8Map = globalParams->getUtf8Map(); - Unicode *u; - const int len = TextStringToUCS4(str, &u); + std::vector u = TextStringToUCS4(str); std::string convertedStr; - for (int i = 0; i < len; ++i) { + for (auto &c : u) { char buf[8]; - const int n = utf8Map->mapUnicode(u[i], buf, sizeof(buf)); + const int n = utf8Map->mapUnicode(c, buf, sizeof(buf)); convertedStr.append(buf, n); } - gfree(u); return convertedStr; } @@ -260,7 +322,24 @@ int main(int argc, char *argv[]) return 0; } - SignatureHandler::setNSSDir(nssDir); + if (strlen(backendString) > 0) { + auto backend = CryptoSign::Factory::typeFromString(backendString); + if (backend) { + CryptoSign::Factory::setPreferredBackend(backend.value()); + } else { + fprintf(stderr, "Unsupported backend\n"); + return 98; + } + } + + if (printCryptoSignBackends) { + print_backends(); + return 0; + } + +#ifdef ENABLE_NSS3 + NSSSignatureConfiguration::setNSSDir(nssDir); +#endif if (listNicknames) { bool getCertsError; @@ -274,7 +353,8 @@ int main(int argc, char *argv[]) printf("Certificate nicknames available:\n"); for (auto &cert : vCerts) { const GooString &nick = cert->getNickName(); - printf("%s\n", nick.c_str()); + const auto location = locationToString(cert->getKeyLocation()); + printf("%s %s %s\n", nick.c_str(), (cert->isQualified() ? "(*)" : " "), location.c_str()); } } } @@ -354,8 +434,7 @@ int main(int argc, char *argv[]) return 2; } - const char *pw = (strlen(password) == 0) ? nullptr : password; - const auto rs = std::unique_ptr(reason.toStr().empty() ? nullptr : utf8ToUtf16WithBom(reason.toStr())); + const auto rs = std::unique_ptr(reason.toStr().empty() ? nullptr : std::make_unique(utf8ToUtf16WithBom(reason.toStr()))); if (newSignatureFieldName.getLength() == 0) { // Create a random field name, it could be anything but 32 hex numbers should @@ -370,10 +449,10 @@ int main(int argc, char *argv[]) } // We don't provide a way to customize the UI from pdfsig for now - const bool success = doc->sign(argv[2], certNickname, pw, newSignatureFieldName.copy(), /*page*/ 1, + const auto failure = doc->sign(std::string { argv[2] }, std::string { certNickname }, std::string { password }, newSignatureFieldName.copy(), /*page*/ 1, /*rect */ { 0, 0, 0, 0 }, /*signatureText*/ {}, /*signatureTextLeft*/ {}, /*fontSize */ 0, /*leftFontSize*/ 0, /*fontColor*/ {}, /*borderWidth*/ 0, /*borderColor*/ {}, /*backgroundColor*/ {}, rs.get(), /* location */ nullptr, /* image path */ "", ownerPW, userPW); - return success ? 0 : 3; + return !failure.has_value() ? 0 : 3; } const std::vector signatures = doc->getSignatureFields(); @@ -416,6 +495,12 @@ int main(int argc, char *argv[]) return 2; } + if (digestName != std::string("SHA256")) { + printf("Only digest SHA256 is supported at the moment\n"); + printf("Please file a bug report if this is important for you\n"); + return 2; + } + bool getCertsError; // We need to call this otherwise NSS spins forever getAvailableSigningCertificates(&getCertsError); @@ -431,17 +516,36 @@ int main(int argc, char *argv[]) return 2; } if (etsiCAdESdetached) { - ffs->setSignatureType(ETSI_CAdES_detached); + ffs->setSignatureType(CryptoSign::SignatureType::ETSI_CAdES_detached); } - const char *pw = (strlen(password) == 0) ? nullptr : password; - const auto rs = std::unique_ptr(reason.toStr().empty() ? nullptr : utf8ToUtf16WithBom(reason.toStr())); + const auto rs = std::unique_ptr(reason.toStr().empty() ? nullptr : std::make_unique(utf8ToUtf16WithBom(reason.toStr()))); if (ffs->getNumWidgets() != 1) { printf("Unexpected number of widgets for the signature: %d\n", ffs->getNumWidgets()); return 2; } +#ifdef HAVE_GETTEXT + if (!noAppearance) { + setlocale(LC_ALL, ""); + bindtextdomain("pdfsig", CMAKE_INSTALL_LOCALEDIR); + textdomain("pdfsig"); + } +#endif FormWidgetSignature *fws = static_cast(ffs->getWidget(0)); - const bool success = fws->signDocument(argv[2], certNickname, digestName, pw, rs.get()); - return success ? 0 : 3; + auto backend = CryptoSign::Factory::createActive(); + auto sigHandler = backend->createSigningHandler(certNickname, HashAlgorithm::Sha256); + std::unique_ptr certInfo = sigHandler->getCertificateInfo(); + if (!certInfo) { + fprintf(stderr, "signDocument: error getting signature info\n"); + return 2; + } + const std::string signerName = certInfo->getSubjectInfo().commonName; + const std::string timestamp = timeToStringWithFormat(nullptr, "%Y.%m.%d %H:%M:%S %z"); + const AnnotColor blackColor(0, 0, 0); + const std::string signatureText(GooString::format(_("Digitally signed by {0:s}"), signerName.c_str()) + "\n" + GooString::format(_("Date: {0:s}"), timestamp.c_str())); + const auto gSignatureText = std::make_unique((signatureText.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(signatureText)); + const auto gSignatureLeftText = std::make_unique((signerName.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(signerName)); + const auto failure = fws->signDocumentWithAppearance(argv[2], std::string { certNickname }, std::string { password }, rs.get(), nullptr, {}, {}, *gSignatureText, *gSignatureLeftText, 0, 0, std::make_unique(blackColor)); + return !failure.has_value() ? 0 : 3; } if (argc > 2) { @@ -456,7 +560,10 @@ int main(int argc, char *argv[]) for (unsigned int i = 0; i < sigCount; i++) { const bool dumpingOk = dumpSignature(i, sigCount, signatures.at(i), fileName->c_str()); if (!dumpingOk) { - return 3; + // for now, do nothing. We have logged a message + // to the user before returning false in dumpSignature + // and it is possible to have "holes" in the signatures + continue; } } return 0; @@ -467,6 +574,16 @@ int main(int argc, char *argv[]) printf("File '%s' does not contain any signatures\n", fileName->c_str()); return 2; } + std::unordered_map signatureInfos; + for (unsigned int i = 0; i < sigCount; i++) { + // Let's start the signature check first for signatures. + // we can always wait for completion later + FormFieldSignature *ffs = signatures.at(i); + if (ffs->getSignatureType() == CryptoSign::SignatureType::unsigned_signature_field) { + continue; + } + signatureInfos[i] = ffs->validateSignatureAsync(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch, {}); + } for (unsigned int i = 0; i < sigCount; i++) { FormFieldSignature *ffs = signatures.at(i); @@ -477,36 +594,37 @@ int main(int argc, char *argv[]) printf(" - Signature Field Name: %s\n", name.c_str()); } - if (ffs->getSignatureType() == unsigned_signature_field) { + if (ffs->getSignatureType() == CryptoSign::SignatureType::unsigned_signature_field) { printf(" The signature form field is not signed.\n"); continue; } - const SignatureInfo *sig_info = ffs->validateSignature(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch); - printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName()); - printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN()); + const SignatureInfo *sig_info = signatureInfos[i]; + CertificateValidationStatus certificateStatus = ffs->validateSignatureResult(); + printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName().c_str()); + printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN().c_str()); printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime())); printf(" - Signing Hash Algorithm: "); switch (sig_info->getHashAlgorithm()) { - case HASH_AlgMD2: + case HashAlgorithm::Md2: printf("MD2\n"); break; - case HASH_AlgMD5: + case HashAlgorithm::Md5: printf("MD5\n"); break; - case HASH_AlgSHA1: + case HashAlgorithm::Sha1: printf("SHA1\n"); break; - case HASH_AlgSHA256: + case HashAlgorithm::Sha256: printf("SHA-256\n"); break; - case HASH_AlgSHA384: + case HashAlgorithm::Sha384: printf("SHA-384\n"); break; - case HASH_AlgSHA512: + case HashAlgorithm::Sha512: printf("SHA-512\n"); break; - case HASH_AlgSHA224: + case HashAlgorithm::Sha224: printf("SHA-224\n"); break; default: @@ -514,16 +632,20 @@ int main(int argc, char *argv[]) } printf(" - Signature Type: "); switch (ffs->getSignatureType()) { - case adbe_pkcs7_sha1: + case CryptoSign::SignatureType::adbe_pkcs7_sha1: printf("adbe.pkcs7.sha1\n"); break; - case adbe_pkcs7_detached: + case CryptoSign::SignatureType::adbe_pkcs7_detached: printf("adbe.pkcs7.detached\n"); break; - case ETSI_CAdES_detached: + case CryptoSign::SignatureType::ETSI_CAdES_detached: printf("ETSI.CAdES.detached\n"); break; - default: + case CryptoSign::SignatureType::g10c_pgp_signature_detached: + printf("g10c.pgp.signature.detached\n"); + break; + case CryptoSign::SignatureType::unknown_signature_type: + case CryptoSign::SignatureType::unsigned_signature_field: /*shouldn't happen*/ printf("unknown\n"); } const std::vector ranges = ffs->getSignedRangeBounds(); @@ -542,7 +664,7 @@ int main(int argc, char *argv[]) if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) { continue; } - printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus())); + printf(" - Certificate Validation: %s\n", getReadableCertState(certificateStatus)); } return 0; diff --git a/utils/pdftocairo.1 b/utils/pdftocairo.1 index 8de2392..782f81f 100644 --- a/utils/pdftocairo.1 +++ b/utils/pdftocairo.1 @@ -211,6 +211,10 @@ Generate Level 2 PostScript (PS only). Generate Level 3 PostScript (PS only). This enables all Level 2 features plus shading patterns and masked images. This is the default setting. .TP +.B \-struct +If the input file contains structural information about the document's content, +write this information to the output file (PDF only). +.TP .B \-origpagesizes This option is the same as "\-paper match". .TP diff --git a/utils/pdftocairo.cc b/utils/pdftocairo.cc index 8a5f08f..5c279bf 100644 --- a/utils/pdftocairo.cc +++ b/utils/pdftocairo.cc @@ -19,7 +19,7 @@ // Copyright (C) 2009 Shen Liang // Copyright (C) 2009 Stefan Thomas // Copyright (C) 2009, 2010, 2017-2020, 2022 Albert Astals Cid -// Copyright (C) 2010, 2011-2017 Adrian Johnson +// Copyright (C) 2010, 2011-2017, 2023, 2024 Adrian Johnson // Copyright (C) 2010, 2014 Hib Eris // Copyright (C) 2010 Jonathan Liu // Copyright (C) 2010 William Bader @@ -40,6 +40,7 @@ // Copyright (C) 2021 Peter Williams // Copyright (C) 2021 Christian Persch // Copyright (C) 2022 James Cloos +// Copyright (C) 2023 Anton Thomasson // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -131,6 +132,7 @@ static bool noShrink = false; static bool noCenter = false; static bool duplex = false; static char tiffCompressionStr[16] = ""; +static bool docStruct = false; static char ownerPassword[33] = ""; static char userPassword[33] = ""; @@ -219,6 +221,10 @@ static const ArgDesc argDesc[] = { { "-nocenter", argFlag, &noCenter, 0, "don't center pages smaller than the paper size" }, { "-duplex", argFlag, &duplex, 0, "enable duplex printing" }, +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0) + { "-struct", argFlag, &docStruct, 0, "enable logical document structure" }, +#endif + { "-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)" }, { "-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)" }, @@ -626,10 +632,10 @@ static void beginDocument(GooString *inputFileName, GooString *outputFileName, d } } -static void beginPage(double *w, double *h) +static void beginPage(double *w, double *h) // NOLINT(readability-non-const-parameter) On the windows codepath can't be const { if (printing) { - if (ps || eps) { + if (ps) { #ifdef CAIRO_HAS_PS_SURFACE if (*w > *h) { cairo_ps_surface_dsc_comment(surface, "%%PageOrientation: Landscape"); @@ -724,11 +730,25 @@ static void renderPage(PDFDoc *doc, CairoOutputDev *cairoOut, int pg, double pag cairo_destroy(cr); } -static void endPage(GooString *imageFileName) +static void endPage(GooString *imageFileName, CairoOutputDev *cairoOut, bool isLastPage) { cairo_status_t status; + cairo_t *cr; if (printing) { + if (isLastPage) { + cr = cairo_create(surface); + cairoOut->setCairo(cr); + cairoOut->setPrinting(printing); + cairoOut->emitStructTree(); + cairoOut->setCairo(nullptr); + status = cairo_status(cr); + if (status) { + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + } + cairo_destroy(cr); + } + cairo_surface_show_page(surface); #ifdef CAIRO_HAS_WIN32_SURFACE @@ -1024,6 +1044,10 @@ int main(int argc, char *argv[]) level3 = true; } + if (docStruct && !pdf) { + fprintf(stderr, "Error: -struct may only be used with pdf or output.\n"); + exit(99); + } if (eps && (origPageSizes || paperSize[0] || paperWidth > 0 || paperHeight > 0)) { fprintf(stderr, "Error: page size options may not be used with eps output.\n"); exit(99); @@ -1143,8 +1167,15 @@ int main(int argc, char *argv[]) // If our page range selection and document size indicate we're only // outputting a single page, ensure that even/odd page selection doesn't - // filter out that single page. - if (firstPage == lastPage && ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0))) { + // filter out that single page. Also adjust first and last page so there are no pages + // skipped at the start or end of the for loop. + if ((printOnlyEven && firstPage % 2 == 1) || (printOnlyOdd && firstPage % 2 == 0)) { + firstPage++; + } + if ((printOnlyEven && lastPage % 2 == 1) || (printOnlyOdd && lastPage % 2 == 0)) { + lastPage--; + } + if (lastPage < firstPage) { fprintf(stderr, "Invalid even/odd page selection, no pages match criteria.\n"); exit(99); } @@ -1172,6 +1203,8 @@ int main(int argc, char *argv[]) #endif cairoOut = new CairoOutputDev(); + cairoOut->setLogicalStructure(docStruct); + #ifdef USE_CMS cairoOut->setDisplayProfile(profile); #endif @@ -1238,24 +1271,16 @@ int main(int argc, char *argv[]) } beginPage(&output_w, &output_h); renderPage(doc.get(), cairoOut, pg, pg_w, pg_h, output_w, output_h); - endPage(imageFileName); + endPage(imageFileName, cairoOut, pg == lastPage); } endDocument(); // clean up delete cairoOut; - if (fileName) { - delete fileName; - } - if (outputName) { - delete outputName; - } - if (outputFileName) { - delete outputFileName; - } - if (imageFileName) { - delete imageFileName; - } + delete fileName; + delete outputName; + delete outputFileName; + delete imageFileName; #ifdef USE_CMS if (icc_data) { @@ -1263,5 +1288,12 @@ int main(int argc, char *argv[]) } #endif +#ifndef NDEBUG + // Clear the cairo font cache. If all references to font faces or + // scaled fonts have not been released this function will + // assert. If this occurs we have found a memory leak. + cairo_debug_reset_static_data(); +#endif + return 0; } diff --git a/utils/pdftohtml.cc b/utils/pdftohtml.cc index 97b141a..d408b5c 100644 --- a/utils/pdftohtml.cc +++ b/utils/pdftohtml.cc @@ -13,7 +13,7 @@ // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // -// Copyright (C) 2007-2008, 2010, 2012, 2015-2020, 2022 Albert Astals Cid +// Copyright (C) 2007-2008, 2010, 2012, 2015-2020, 2022, 2024 Albert Astals Cid // Copyright (C) 2010 Hib Eris // Copyright (C) 2010 Mike Slegeir // Copyright (C) 2010, 2013 Suzuki Toshiya @@ -28,8 +28,9 @@ // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Thibaut Brard // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019, 2021 Oliver Sander +// Copyright (C) 2019, 2021, 2024 Oliver Sander // Copyright (C) 2021 Hubert Figuiere +// Copyright (C) 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git @@ -71,6 +72,7 @@ #include "goo/gfile.h" #include "Win32Console.h" #include "InMemoryFile.h" +#include "UTF.h" static int firstPage = 1; static int lastPage = 0; @@ -99,7 +101,7 @@ static char ownerPassword[33] = ""; static char userPassword[33] = ""; static bool printVersion = false; -static GooString *getInfoString(Dict *infoDict, const char *key); +static std::unique_ptr getInfoString(Dict *infoDict, const char *key); static GooString *getInfoDate(Dict *infoDict, const char *key); static char textEncName[128] = ""; @@ -158,9 +160,12 @@ int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName = nullptr; - GooString *docTitle = nullptr; - GooString *author = nullptr, *keywords = nullptr, *subject = nullptr, *date = nullptr; - GooString *htmlFileName = nullptr; + std::unique_ptr docTitle; + std::unique_ptr author; + std::unique_ptr keywords; + std::unique_ptr subject; + GooString *date = nullptr; + std::unique_ptr htmlFileName; HtmlOutputDev *htmlOut = nullptr; SplashOutputDev *splashOut = nullptr; bool doOutline; @@ -241,19 +246,19 @@ int main(int argc, char *argv[]) if (tmp->getLength() >= 5) { const char *p = tmp->c_str() + tmp->getLength() - 5; if (!strcmp(p, ".html") || !strcmp(p, ".HTML")) { - htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 5); + htmlFileName = std::make_unique(tmp->c_str(), tmp->getLength() - 5); } } } else { if (tmp->getLength() >= 4) { const char *p = tmp->c_str() + tmp->getLength() - 4; if (!strcmp(p, ".xml") || !strcmp(p, ".XML")) { - htmlFileName = new GooString(tmp->c_str(), tmp->getLength() - 4); + htmlFileName = std::make_unique(tmp->c_str(), tmp->getLength() - 4); } } } if (!htmlFileName) { - htmlFileName = new GooString(tmp); + htmlFileName = std::make_unique(tmp); } delete tmp; } else if (fileName->cmp("fd://0") == 0) { @@ -262,7 +267,7 @@ int main(int argc, char *argv[]) } else { const char *p = fileName->c_str() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { - htmlFileName = new GooString(fileName->c_str(), fileName->getLength() - 4); + htmlFileName = std::make_unique(fileName->c_str(), fileName->getLength() - 4); } else { htmlFileName = fileName->copy(); } @@ -317,7 +322,7 @@ int main(int argc, char *argv[]) } } if (!docTitle) { - docTitle = new GooString(htmlFileName); + docTitle = htmlFileName->copy(); } if (!singleHtml) { @@ -330,19 +335,7 @@ int main(int argc, char *argv[]) // write text file htmlOut = new HtmlOutputDev(doc->getCatalog(), htmlFileName->c_str(), docTitle->c_str(), author ? author->c_str() : nullptr, keywords ? keywords->c_str() : nullptr, subject ? subject->c_str() : nullptr, date ? date->c_str() : nullptr, rawOrder, firstPage, doOutline); - delete docTitle; - if (author) { - delete author; - } - if (keywords) { - delete keywords; - } - if (subject) { - delete subject; - } - if (date) { - delete date; - } + delete date; if ((complexMode || singleHtml) && !xml && !ignore) { // White paper color @@ -359,10 +352,10 @@ int main(int argc, char *argv[]) doc->displayPage(splashOut, pg, 72 * scale, 72 * scale, 0, true, false, false); SplashBitmap *bitmap = splashOut->getBitmap(); - const std::unique_ptr imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", htmlFileName->c_str(), pg, extension); - auto f1 = dataUrls ? imf.open("wb") : fopen(imgFileName->c_str(), "wb"); + const std::string imgFileName = GooString::format("{0:s}{1:03d}.{2:s}", htmlFileName->c_str(), pg, extension); + auto f1 = dataUrls ? imf.open("wb") : fopen(imgFileName.c_str(), "wb"); if (!f1) { - fprintf(stderr, "Could not open %s\n", imgFileName->c_str()); + fprintf(stderr, "Could not open %s\n", imgFileName.c_str()); continue; } bitmap->writeImgFile(format, f1, 72 * scale, 72 * scale); @@ -370,7 +363,7 @@ int main(int argc, char *argv[]) if (dataUrls) { htmlOut->addBackgroundImage(std::string((format == splashFormatJpeg) ? "data:image/jpeg;base64," : "data:image/png;base64,") + gbase64Encode(imf.getBuffer())); } else { - htmlOut->addBackgroundImage(gbasename(imgFileName->c_str())); + htmlOut->addBackgroundImage(gbasename(imgFileName.c_str())); } } @@ -390,14 +383,10 @@ int main(int argc, char *argv[]) error: delete fileName; - if (htmlFileName) { - delete htmlFileName; - } - return exit_status; } -static GooString *getInfoString(Dict *infoDict, const char *key) +static std::unique_ptr getInfoString(Dict *infoDict, const char *key) { Object obj; // Raw value as read from PDF (may be in pdfDocEncoding or UCS2) @@ -406,7 +395,7 @@ static GooString *getInfoString(Dict *infoDict, const char *key) Unicode *unicodeString; int unicodeLength; // Value HTML escaped and converted to desired encoding - GooString *encodedString = nullptr; + std::unique_ptr encodedString; // Is rawString UCS2 (as opposed to pdfDocEncoding) bool isUnicode; @@ -415,7 +404,7 @@ static GooString *getInfoString(Dict *infoDict, const char *key) rawString = obj.getString(); // Convert rawString to unicode - if (rawString->hasUnicodeMarker()) { + if (hasUnicodeByteOrderMark(rawString->toStr())) { isUnicode = true; unicodeLength = (obj.getString()->getLength() - 2) / 2; } else { diff --git a/utils/pdftops.cc b/utils/pdftops.cc index 89a53ba..d9f6692 100644 --- a/utils/pdftops.cc +++ b/utils/pdftops.cc @@ -25,7 +25,7 @@ // Copyright (C) 2013 Suzuki Toshiya // Copyright (C) 2014, 2017 Adrian Johnson // Copyright (C) 2018 Adam Reichold -// Copyright (C) 2019, 2021 Oliver Sander +// Copyright (C) 2019, 2021, 2023 Oliver Sander // Copyright (C) 2020 Philipp Knechtges // Copyright (C) 2021 Hubert Figuiere // @@ -194,7 +194,7 @@ int main(int argc, char *argv[]) { std::unique_ptr doc; GooString *fileName; - GooString *psFileName; + std::string psFileName; PSLevel level; PSOutMode mode; std::optional ownerPW, userPW; @@ -391,18 +391,19 @@ int main(int argc, char *argv[]) // construct PostScript file name if (argc == 3) { - psFileName = new GooString(argv[2]); + psFileName = std::string(argv[2]); } else if (fileName->cmp("fd://0") == 0) { error(errCommandLine, -1, "You have to provide an output filename when reading from stdin."); goto err1; } else { const char *p = fileName->c_str() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { - psFileName = new GooString(fileName->c_str(), fileName->getLength() - 4); + psFileName = std::string(fileName->c_str(), fileName->getLength() - 4); + } else { - psFileName = fileName->copy(); + psFileName = fileName->toStr(); } - psFileName->append(doEPS ? ".eps" : ".ps"); + psFileName += (doEPS ? ".eps" : ".ps"); } // get page range @@ -414,13 +415,13 @@ int main(int argc, char *argv[]) } if (lastPage < firstPage) { error(errCommandLine, -1, "Wrong page range given: the first page ({0:d}) can not be after the last page ({1:d}).", firstPage, lastPage); - goto err2; + goto err1; } // check for multi-page EPS or form if ((doEPS || doForm) && firstPage != lastPage) { error(errCommandLine, -1, "EPS and form files can only contain one page."); - goto err2; + goto err1; } for (int i = firstPage; i <= lastPage; ++i) { @@ -428,7 +429,7 @@ int main(int argc, char *argv[]) } // write PostScript file - psOut = new PSOutputDev(psFileName->c_str(), doc.get(), nullptr, pages, mode, paperWidth, paperHeight, noCrop, duplex, /*imgLLXA*/ 0, /*imgLLYA*/ 0, + psOut = new PSOutputDev(psFileName.c_str(), doc.get(), nullptr, pages, mode, paperWidth, paperHeight, noCrop, duplex, /*imgLLXA*/ 0, /*imgLLYA*/ 0, /*imgURXA*/ 0, /*imgURYA*/ 0, psRasterizeWhenNeeded, /*manualCtrlA*/ false, /*customCodeCbkA*/ nullptr, /*customCodeCbkDataA*/ nullptr, level); if (noCenter) { psOut->setPSCenter(false); @@ -496,15 +497,13 @@ int main(int argc, char *argv[]) } else { delete psOut; exitCode = 2; - goto err2; + goto err1; } delete psOut; exitCode = 0; // clean up -err2: - delete psFileName; err1: delete fileName; err0: diff --git a/utils/pdftotext.1 b/utils/pdftotext.1 index 8636015..c34c98a 100644 --- a/utils/pdftotext.1 +++ b/utils/pdftotext.1 @@ -6,8 +6,8 @@ pdftotext \- Portable Document Format (PDF) to text converter .SH SYNOPSIS .B pdftotext [options] -.RI [ PDF-file -.RI [ text-file ]] +.RI PDF-file +.RI [ text-file ] .SH DESCRIPTION .B Pdftotext converts Portable Document Format (PDF) files to plain text. @@ -22,7 +22,7 @@ is not specified, pdftotext converts .I file.pdf to .IR file.txt . -If +If .I text-file is \'-', the text is sent to stdout. If .I PDF-file diff --git a/utils/pdftotext.cc b/utils/pdftotext.cc index c1d4bc8..50f02f5 100644 --- a/utils/pdftotext.cc +++ b/utils/pdftotext.cc @@ -16,7 +16,7 @@ // under GPL version 2 or later // // Copyright (C) 2006 Dominic Lachowicz -// Copyright (C) 2007-2008, 2010, 2011, 2017-2022 Albert Astals Cid +// Copyright (C) 2007-2008, 2010, 2011, 2017-2022, 2024 Albert Astals Cid // Copyright (C) 2009 Jan Jockusch // Copyright (C) 2010, 2013 Hib Eris // Copyright (C) 2010 Kenneth Berland @@ -274,7 +274,7 @@ int main(int argc, char *argv[]) if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { textFileName = std::make_unique(fileName.c_str(), fileName.getLength() - 4); } else { - textFileName.reset(fileName.copy()); + textFileName = fileName.copy(); } textFileName->append(htmlMeta ? ".html" : ".txt"); } @@ -301,7 +301,7 @@ int main(int argc, char *argv[]) return 2; } } - fputs("", f); + fputs(R"()", f); fputs("\n", f); fputs("\n", f); info = doc->getDocInfo(); @@ -312,13 +312,13 @@ int main(int argc, char *argv[]) } else { fputs("\n", f); } - printInfoString(f, info.getDict(), "Subject", "\n", uMap); - printInfoString(f, info.getDict(), "Keywords", "\n", uMap); - printInfoString(f, info.getDict(), "Author", "\n", uMap); - printInfoString(f, info.getDict(), "Creator", "\n", uMap); - printInfoString(f, info.getDict(), "Producer", "\n", uMap); - printInfoDate(f, info.getDict(), "CreationDate", "\n"); - printInfoDate(f, info.getDict(), "ModDate", "\n"); + printInfoString(f, info.getDict(), "Subject", R"(\n", uMap); + printInfoString(f, info.getDict(), "Keywords", R"(\n", uMap); + printInfoString(f, info.getDict(), "Author", R"(\n", uMap); + printInfoString(f, info.getDict(), "Creator", R"(\n", uMap); + printInfoString(f, info.getDict(), "Producer", R"(\n", uMap); + printInfoDate(f, info.getDict(), "CreationDate", R"(\n"); + printInfoDate(f, info.getDict(), "ModDate", R"(\n"); } fputs("\n", f); fputs("\n", f); diff --git a/utils/pdfunite.cc b/utils/pdfunite.cc index 8308254..28dd5ca 100644 --- a/utils/pdfunite.cc +++ b/utils/pdfunite.cc @@ -7,15 +7,16 @@ // Copyright (C) 2011-2015, 2017 Thomas Freitag // Copyright (C) 2012 Arseny Solokha // Copyright (C) 2012 Fabio D'Urso -// Copyright (C) 2012, 2014, 2017-2019, 2021, 2022 Albert Astals Cid +// Copyright (C) 2012, 2014, 2017-2019, 2021, 2022, 2024 Albert Astals Cid // Copyright (C) 2013 Adrian Johnson // Copyright (C) 2013 Hib Eris // Copyright (C) 2015 Arthur Stavisky // Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, . Work sponsored by the LiMux project of the city of Munich // Copyright (C) 2018 Adam Reichold // Copyright (C) 2019 Marek Kasik -// Copyright (C) 2019 Oliver Sander +// Copyright (C) 2019, 2023 Oliver Sander // Copyright (C) 2022 crt +// Copyright (C) 2025 g10 Code GmbH, Author: Sune Stolborg Vuorela // //======================================================================== @@ -48,8 +49,8 @@ static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldR const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); if (mkey.isString() && mvalue.isRef()) { if (mkey.getString()->cmp(key.getString()) < 0) { - newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); - newNameArray->add(Object({ mvalue.getRef().num + numOffset, mvalue.getRef().gen })); + newNameArray->add(Object(mkey.getString()->copy())); + newNameArray->add(Object(Ref { mvalue.getRef().num + numOffset, mvalue.getRef().gen })); j += 2; } else if (mkey.getString()->cmp(key.getString()) == 0) { j += 2; @@ -60,7 +61,7 @@ static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldR j += 2; } } - newNameArray->add(Object(new GooString(key.getString()->c_str()))); + newNameArray->add(Object(key.getString()->copy())); newNameArray->add(Object(value.getRef())); } } @@ -68,8 +69,8 @@ static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldR const Object &mkey = mergeNameArray.arrayGetNF(j); const Object &mvalue = mergeNameArray.arrayGetNF(j + 1); if (mkey.isString() && mvalue.isRef()) { - newNameArray->add(Object(new GooString(mkey.getString()->c_str()))); - newNameArray->add(Object({ mvalue.getRef().num + numOffset, mvalue.getRef().gen })); + newNameArray->add(Object(mkey.getString()->copy())); + newNameArray->add(Object(Ref { mvalue.getRef().num + numOffset, mvalue.getRef().gen })); } j += 2; } @@ -81,8 +82,8 @@ static void doMergeNameTree(PDFDoc *doc, XRef *srcXRef, XRef *countRef, int oldR const Object &key = mergeNameArray.arrayGetNF(i); const Object &value = mergeNameArray.arrayGetNF(i + 1); if (key.isString() && value.isRef()) { - newNameArray->add(Object(new GooString(key.getString()->c_str()))); - newNameArray->add(Object({ value.getRef().num + numOffset, value.getRef().gen })); + newNameArray->add(Object(key.getString()->copy())); + newNameArray->add(Object(Ref { value.getRef().num + numOffset, value.getRef().gen })); } } srcNameTree->add("Names", Object(newNameArray)); @@ -117,7 +118,7 @@ static bool doMergeFormDict(Dict *srcFormDict, Dict *mergeFormDict, int numOffse error(errSyntaxError, -1, "Fields object is not a Ref."); return false; } - srcFields.arrayAdd(Object({ value.getRef().num + numOffset, value.getRef().gen })); + srcFields.arrayAdd(Object(Ref { value.getRef().num + numOffset, value.getRef().gen })); } } return true; @@ -201,7 +202,7 @@ int main(int argc, char *argv[]) Object names; Object afObj; Object ocObj; - if (docs.size() >= 1) { + if (!docs.empty()) { Object catObj = docs[0]->getXRef()->getCatalog(); if (!catObj.isDict()) { fclose(f); diff --git a/utils/po/CMakeLists.txt b/utils/po/CMakeLists.txt new file mode 100644 index 0000000..ef36177 --- /dev/null +++ b/utils/po/CMakeLists.txt @@ -0,0 +1,38 @@ +file(GLOB _files "${CMAKE_CURRENT_SOURCE_DIR}/*") +foreach (_dir ${_files}) + if (IS_DIRECTORY "${_dir}" AND EXISTS "${_dir}/CMakeLists.txt") + add_subdirectory("${_dir}") + get_filename_component(_lang ${_dir} NAME) + list(APPEND _langs ${_lang}) + endif () +endforeach() + + +macro(UPDATE_POT_FILE input) + set(_potFile "${input}.pot") + foreach(_lang ${_langs}) + set(_poFile "${CMAKE_CURRENT_SOURCE_DIR}/${_lang}/${input}.po") + list(APPEND _commands + COMMAND ${GETTEXT_MSGMERGE_EXECUTABLE} --quiet --update --backup=none -F ${_poFile} "${CMAKE_CURRENT_SOURCE_DIR}/${_potFile}") + endforeach() + + set(COPY_NEW_POTFILE ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${_potFile}.new ${CMAKE_CURRENT_SOURCE_DIR}/${_potFile}) + if (NOT (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows")) + # IF the new potfile carries changes other than an updated POT-creation-date (autogenerated) THEN proceed to use that new potfile + set(CM diff --brief --ignore-matching-lines="POT-Creation-Date.*" ${CMAKE_CURRENT_SOURCE_DIR}/${_potFile} ${CMAKE_CURRENT_BINARY_DIR}/${_potFile}.new || ${COPY_NEW_POTFILE}) + else () + set(CM ${COPY_NEW_POTFILE}) + endif () + + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_potFile}.new + COMMAND xgettext ${input}.cc --package-name=${input} --keyword=_ -F --output="${CMAKE_CURRENT_BINARY_DIR}/${_potFile}.new" + COMMAND ${CM} + ${_commands} + DEPENDS ../${input}.cc + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/..) + add_custom_target(update_pot_${input} + ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_potFile}.new) +endmacro() + +UPDATE_POT_FILE(pdfsig) diff --git a/utils/po/ca/CMakeLists.txt b/utils/po/ca/CMakeLists.txt new file mode 100644 index 0000000..3225131 --- /dev/null +++ b/utils/po/ca/CMakeLists.txt @@ -0,0 +1,4 @@ +get_filename_component(_lang ${CMAKE_CURRENT_SOURCE_DIR} NAME) +gettext_process_po_files(${_lang} ALL INSTALL_DESTINATION ${CMAKE_INSTALL_LOCALEDIR} PO_FILES + pdfsig.po +) diff --git a/utils/po/ca/pdfsig.po b/utils/po/ca/pdfsig.po new file mode 100644 index 0000000..0d83e42 --- /dev/null +++ b/utils/po/ca/pdfsig.po @@ -0,0 +1,28 @@ +# Translation of pdfsig.po to Catalan +# Copyright (C) 2024, Josep M. Ferrer +# This file is distributed under the same license as the pdfsig package. +# +msgid "" +msgstr "" +"Project-Id-Version: pdfsig\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-12 14:44-0700\n" +"PO-Revision-Date: 2024-03-04 23:01+0100\n" +"Last-Translator: Josep M. Ferrer \n" +"Language-Team: Catalan \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Lokalize 22.12.3\n" + +#: pdfsig.cc:544 +#, c++-format +msgid "Date: {0:s}" +msgstr "Data: {0:s}" + +#: pdfsig.cc:544 +#, c++-format +msgid "Digitally signed by {0:s}" +msgstr "Signat digitalment per {0:s}" diff --git a/utils/po/pdfsig.pot b/utils/po/pdfsig.pot new file mode 100644 index 0000000..ffe3bc2 --- /dev/null +++ b/utils/po/pdfsig.pot @@ -0,0 +1,28 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the pdfsig package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: pdfsig\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-12 14:44-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: pdfsig.cc:544 +#, c++-format +msgid "Date: {0:s}" +msgstr "" + +#: pdfsig.cc:544 +#, c++-format +msgid "Digitally signed by {0:s}" +msgstr "" diff --git a/utils/printencodings.cc b/utils/printencodings.cc index 3e727c7..7ff6e83 100644 --- a/utils/printencodings.cc +++ b/utils/printencodings.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2019, 2021, Albert Astals Cid + * Copyright (C) 2008, 2019, 2021, 2024, Albert Astals Cid * Copyright (C) 2017, Adrian Johnson * Copyright (C) 2018, Adam Reichold * Copyright (C) 2019, Oliver Sander @@ -30,7 +30,7 @@ void printEncodings() { std::vector encNames = globalParams->getEncodingNames(); - std::sort(encNames.begin(), encNames.end()); + std::ranges::sort(encNames); printf("Available encodings are:\n"); for (const std::string &enc : encNames) {

    yMin), xoutRound(tmp1->xMin)); if (!singleHtml) { fputc('0', pageFile); } else { @@ -938,7 +933,7 @@ void HtmlPage::dump(FILE *f, int pageNum, const std::vector &backgr style_index += 2; // yFlip } - fprintf(f, "
    \n", styles[style_index], img->fName->c_str()); + fprintf(f, "
    \n", styles[style_index], img->fName.c_str()); delete img; } imgList.clear(); @@ -986,7 +981,7 @@ void HtmlPage::setDocName(const char *fname) DocName = new GooString(fname); } -void HtmlPage::addImage(std::unique_ptr &&fname, GfxState *state) +void HtmlPage::addImage(std::string &&fname, GfxState *state) { HtmlImage *img = new HtmlImage(std::move(fname), state); imgList.push_back(img); @@ -1126,7 +1121,7 @@ HtmlOutputDev::HtmlOutputDev(Catalog *catalogA, const char *fileName, const char fputs("\n\n\n\n\n", fContentsFrame); if (doOutline) { - fprintf(fContentsFrame, "
    Outline
    ", gbasename(Docname->c_str()).c_str(), complexMode ? "-outline.html" : "s.html#outline"); + fprintf(fContentsFrame, R"(Outline
    )", gbasename(Docname->c_str()).c_str(), complexMode ? "-outline.html" : "s.html#outline"); } } if (!complexMode) { /* not in complex mode */ @@ -1207,9 +1202,7 @@ HtmlOutputDev::~HtmlOutputDev() fclose(page); } } - if (pages) { - delete pages; - } + delete pages; } void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) @@ -1225,10 +1218,10 @@ void HtmlOutputDev::startPage(int pageNumA, GfxState *state, XRef *xref) exit(1); } delete fname; - // if(state->getRotation()!=0) + // if(state->getRotation()!=0) // fprintf(tin,"ROTATE=%d rotate %d neg %d neg translate\n",state->getRotation(),state->getX1(),-state->getY1()); - // else - fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1()); + // else + fprintf(tin,"ROTATE=%d neg %d neg translate\n",state->getX1(),state->getY1()); } } #endif @@ -1309,16 +1302,20 @@ void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str) int c; // open the image file - std::unique_ptr fName = createImageFileName("jpg"); - f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); + std::string fName = createImageFileName("jpg"); + f1 = dataUrls ? ims.open("wb") : fopen(fName.c_str(), "wb"); if (!f1) { - error(errIO, -1, "Couldn't open image file '{0:t}'", fName.get()); + error(errIO, -1, "Couldn't open image file '{0:s}'", fName.c_str()); return; } // initialize stream str = str->getNextStream(); - str->reset(); + if (!str->reset()) { + fclose(f1); + error(errIO, -1, "Couldn't reset stream"); + return; + } // copy the stream while ((c = str->getChar()) != EOF) { @@ -1328,7 +1325,7 @@ void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str) fclose(f1); if (dataUrls) { - fName = std::make_unique(std::string("data:image/jpeg;base64,") + gbase64Encode(ims.getBuffer())); + fName = std::string("data:image/jpeg;base64,") + gbase64Encode(ims.getBuffer()); } pages->addImage(std::move(fName), state); } @@ -1336,7 +1333,7 @@ void HtmlOutputDev::drawJpegImage(GfxState *state, Stream *str) void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool isMask) { #ifdef ENABLE_LIBPNG - FILE *f1; + std::unique_ptr f1 { nullptr, &fclose }; InMemoryFile ims; if (!colorMap && !isMask) { @@ -1345,43 +1342,41 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he } // open the image file - std::unique_ptr fName = createImageFileName("png"); - f1 = dataUrls ? ims.open("wb") : fopen(fName->c_str(), "wb"); + std::string fName = createImageFileName("png"); + f1.reset(dataUrls ? ims.open("wb") : fopen(fName.c_str(), "wb")); if (!f1) { - error(errIO, -1, "Couldn't open image file '{0:t}'", fName.get()); + error(errIO, -1, "Couldn't open image file '{0:s}'", fName.c_str()); return; } - PNGWriter *writer = new PNGWriter(isMask ? PNGWriter::MONOCHROME : PNGWriter::RGB); + PNGWriter writer { isMask ? PNGWriter::MONOCHROME : PNGWriter::RGB }; // TODO can we calculate the resolution of the image? - if (!writer->init(f1, width, height, 72, 72)) { - error(errInternal, -1, "Can't init PNG for image '{0:t}'", fName.get()); - delete writer; - fclose(f1); + if (!writer.init(f1.get(), width, height, 72, 72)) { + error(errInternal, -1, "Can't init PNG for image '{0:s}'", fName.c_str()); return; } if (!isMask) { unsigned char *p; GfxRGB rgb; + ImageStream imgStr { str, width, colorMap->getNumPixelComps(), colorMap->getBits() }; + if (!imgStr.reset()) { + error(errInternal, -1, "Can't reset image stream"); + return; + } unsigned char *row = (unsigned char *)gmalloc(3 * width); // 3 bytes/pixel: RGB unsigned char **row_pointer = &row; // Initialize the image stream - ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); - imgStr->reset(); // For each line... for (int y = 0; y < height; y++) { // Convert into a PNG row - p = imgStr->getLine(); + p = imgStr.getLine(); if (!p) { - error(errIO, -1, "Failed to read PNG. '{0:t}' will be incorrect", fName.get()); + error(errIO, -1, "Failed to read PNG. '{0:s}' will be incorrect", fName.c_str()); gfree(row); - delete writer; - delete imgStr; - fclose(f1); return; } for (int x = 0; x < width; x++) { @@ -1393,17 +1388,14 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he p += colorMap->getNumPixelComps(); } - if (!writer->writeRow(row_pointer)) { - error(errIO, -1, "Failed to write into PNG '{0:t}'", fName.get()); - delete writer; - delete imgStr; - fclose(f1); + if (!writer.writeRow(row_pointer)) { + error(errIO, -1, "Failed to write into PNG '{0:s}'", fName.c_str()); + gfree(row); return; } } gfree(row); - imgStr->close(); - delete imgStr; + imgStr.close(); } else { // isMask == true int size = (width + 7) / 8; @@ -1422,7 +1414,10 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he } } - str->reset(); + if (!str->reset()) { + error(errInternal, -1, "failed to reset stream"); + return; + } unsigned char *png_row = (unsigned char *)gmalloc(size); for (int ri = 0; ri < height; ++ri) { @@ -1430,10 +1425,8 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he png_row[i] = str->getChar() ^ invert_bits; } - if (!writer->writeRow(&png_row)) { - error(errIO, -1, "Failed to write into PNG '{0:t}'", fName.get()); - delete writer; - fclose(f1); + if (!writer.writeRow(&png_row)) { + error(errIO, -1, "Failed to write into PNG '{0:s}'", fName.c_str()); gfree(png_row); return; } @@ -1444,12 +1437,10 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he str->close(); - writer->close(); - delete writer; - fclose(f1); + writer.close(); if (dataUrls) { - fName = std::make_unique(std::string("data:image/png;base64,") + gbase64Encode(ims.getBuffer())); + fName = std::string("data:image/png;base64,") + gbase64Encode(ims.getBuffer()); } pages->addImage(std::move(fName), state); #else @@ -1457,7 +1448,7 @@ void HtmlOutputDev::drawPngImage(GfxState *state, Stream *str, int width, int he #endif } -std::unique_ptr HtmlOutputDev::createImageFileName(const char *ext) +std::string HtmlOutputDev::createImageFileName(const char *ext) { return GooString::format("{0:s}-{1:d}_{2:d}.{3:s}", Docname->c_str(), pageNum, pages->getNumImages() + 1, ext); } @@ -1514,16 +1505,15 @@ void HtmlOutputDev::doProcessLink(AnnotLink *link) cvtUserToDev(_x2, _y2, &x2, &y2); - GooString *_dest = getLinkDest(link); - HtmlLink t((double)x1, (double)y2, (double)x2, (double)y1, _dest); + std::unique_ptr _dest = getLinkDest(link); + HtmlLink t((double)x1, (double)y2, (double)x2, (double)y1, std::move(_dest)); pages->AddLink(t); - delete _dest; } -GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) +std::unique_ptr HtmlOutputDev::getLinkDest(AnnotLink *link) { if (!link->getAction()) { - return new GooString(); + return std::make_unique(); } switch (link->getAction()->getKind()) { case actionGoTo: { @@ -1537,7 +1527,7 @@ GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) } if (dest) { - GooString *file = new GooString(gbasename(Docname->c_str())); + std::unique_ptr file = std::make_unique(gbasename(Docname->c_str())); if (dest->isPageRef()) { const Ref pageref = dest->getPageRef(); @@ -1569,17 +1559,16 @@ GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) } return file; } else { - return new GooString(); + return std::make_unique(); } } case actionGoToR: { LinkGoToR *ha = (LinkGoToR *)link->getAction(); LinkDest *dest = nullptr; int destPage = 1; - GooString *file = new GooString(); + std::unique_ptr file = std::make_unique(); if (ha->getFileName()) { - delete file; - file = new GooString(ha->getFileName()->c_str()); + file = std::make_unique(ha->getFileName()->c_str()); } if (ha->getDest() != nullptr) { dest = new LinkDest(*ha->getDest()); @@ -1610,14 +1599,13 @@ GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) } case actionURI: { LinkURI *ha = (LinkURI *)link->getAction(); - GooString *file = new GooString(ha->getURI()); - // printf("uri : %s\n",file->c_str()); - return file; + // printf("uri : %s\n",ha->getURI()->c_str()); + return std::make_unique(ha->getURI()); } case actionLaunch: if (printHtml) { LinkLaunch *ha = (LinkLaunch *)link->getAction(); - GooString *file = new GooString(ha->getFileName()->c_str()); + std::unique_ptr file = std::make_unique(ha->getFileName()->c_str()); const char *p = file->c_str() + file->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { file->del(file->getLength() - 4, 4); @@ -1631,7 +1619,7 @@ GooString *HtmlOutputDev::getLinkDest(AnnotLink *link) } // fallthrough default: - return new GooString(); + return std::make_unique(); } } @@ -1672,10 +1660,9 @@ bool HtmlOutputDev::dumpDocOutline(PDFDoc *doc) output = page; fputs("